Compare commits
5 Commits
feat/impl-
...
009-colors
| Author | SHA1 | Date | |
|---|---|---|---|
| 1baff9075c | |||
| 3d6ff645fe | |||
| dc048eba4e | |||
| 05a683388b | |||
| 8d567390e5 |
@@ -9,7 +9,7 @@
|
||||
"id": "box-model-1",
|
||||
"title": "Padding",
|
||||
"description": "Every element in CSS is a box with four layers: content, padding, border, and margin. <strong>Padding</strong> creates breathing room between your content and the box's edge.<br><br>Without padding, text presses against borders awkwardly. Padding makes content readable and visually balanced.<br><br><pre>.card {\n padding: 1rem;\n}</pre>",
|
||||
"task": "This profile card looks cramped. Add <kbd>padding: 1rem</kbd> to <kbd>.card</kbd> so the text has room to breathe.",
|
||||
"task": "The text inside this profile card is pressed right against the edges. Give it some inner breathing room.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -22,7 +22,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Set <kbd>padding: 1rem</kbd>"
|
||||
"message": "Which property adds space between content and the element's edge?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"id": "box-model-2",
|
||||
"title": "Borders",
|
||||
"description": "Borders create visual boundaries around elements. The <kbd>border</kbd> shorthand takes three values: width, style, and color.<br><br>Common styles: <kbd>solid</kbd>, <kbd>dashed</kbd>, <kbd>dotted</kbd>, <kbd>none</kbd>",
|
||||
"task": "Add a subtle left accent to the card with <kbd>border-left: 4px solid steelblue</kbd>.",
|
||||
"task": "This card could use a colored accent line along its left edge.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
|
||||
"message": "Set <kbd>border-left: 4px solid steelblue</kbd>",
|
||||
"message": "Use the shorthand that sets a border on just one side",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -52,7 +52,7 @@
|
||||
"id": "box-model-3",
|
||||
"title": "Margins",
|
||||
"description": "Margins create space <em>outside</em> the element, separating it from neighbors. While padding pushes content inward, margins push other elements away.",
|
||||
"task": "Add space between these two profile cards with <kbd>margin-bottom: 1rem</kbd> on <kbd>.card</kbd>.",
|
||||
"task": "These two profile cards are touching each other. Add some space below each card to separate them.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article><article class=\"card\"><h3>Alex Rivera</h3><p>UX Designer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "margin-bottom", "expected": "1rem" },
|
||||
"message": "Set <kbd>margin-bottom: 1rem</kbd>"
|
||||
"message": "Which property pushes neighboring elements away from the bottom?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"id": "box-model-4",
|
||||
"title": "Box Sizing",
|
||||
"description": "By default, <kbd>width</kbd> only sets the content width. Padding and borders add to the total. This causes layout headaches.<br><br><kbd>box-sizing: border-box</kbd> includes padding and border in the width, making sizing predictable. Most developers apply this to all elements.",
|
||||
"task": "Both cards have <kbd>width: 200px</kbd>. The left uses default sizing (content-box), making it wider than expected. Fix the right card with <kbd>box-sizing: border-box</kbd>.",
|
||||
"task": "Both cards are set to the same width, but the left one overflows because padding and border are added on top. Fix the right card so its size includes padding and border.",
|
||||
"previewHTML": "<div class=\"demo\"><article class=\"card\">Content-box</article><article class=\"card fix\">Border-box</article></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "box-sizing", "expected": "border-box" },
|
||||
"message": "Set <kbd>box-sizing: border-box</kbd>"
|
||||
"message": "Which sizing mode includes padding and border in the element's width?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,7 +94,7 @@
|
||||
"id": "box-model-5",
|
||||
"title": "Padding Shorthand",
|
||||
"description": "Padding accepts 1-4 values:<br>• 1 value: all sides<br>• 2 values: vertical | horizontal<br>• 4 values: top | right | bottom | left",
|
||||
"task": "This button needs more horizontal space than vertical. Set <kbd>padding: 8px 1rem</kbd> (8px top/bottom, 1rem left/right).",
|
||||
"task": "This button feels too tight. Give it more space on the sides than on top and bottom, using the two-value shorthand.",
|
||||
"previewHTML": "<button class=\"btn\">Follow</button>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "padding:\\s*8px\\s+1rem",
|
||||
"message": "Set <kbd>padding: 8px 1rem</kbd>",
|
||||
"message": "Use the two-value shorthand: vertical first, then horizontal",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"id": "box-model-6",
|
||||
"title": "Margin Shorthand",
|
||||
"description": "Margin uses the same shorthand pattern as padding. A common pattern is centering block elements horizontally with <kbd>margin: 0 auto</kbd>.",
|
||||
"task": "Center this card horizontally. Set <kbd>margin: 0 auto</kbd> to auto-calculate equal left/right margins.",
|
||||
"task": "This card is stuck to the left. Center it horizontally using the margin shorthand with auto side margins.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "margin:\\s*0\\s+auto",
|
||||
"message": "Set <kbd>margin: 0 auto</kbd>",
|
||||
"message": "Use the shorthand that auto-calculates equal horizontal margins",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"id": "box-model-7",
|
||||
"title": "Border Radius",
|
||||
"description": "While not part of the classic box model, <kbd>border-radius</kbd> rounds the corners of an element's border box. Use <kbd>50%</kbd> on a square element to create a circle.",
|
||||
"task": "Make the avatar image circular with <kbd>border-radius: 50%</kbd>.",
|
||||
"task": "The square avatar image should appear as a perfect circle.",
|
||||
"previewHTML": "<article class=\"card\"><img class=\"avatar\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='35' r='25' fill='%23666'/%3E%3Ccircle cx='50' cy='90' r='40' fill='%23666'/%3E%3C/svg%3E\" alt=\"Avatar\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -151,7 +151,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "50%" },
|
||||
"message": "Set <kbd>border-radius: 50%</kbd>"
|
||||
"message": "Which property rounds corners? Think about what percentage makes a circle"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"id": "box-model-8",
|
||||
"title": "Complete Card",
|
||||
"description": "Let's combine everything. This notification card needs styling to look polished.",
|
||||
"task": "Style the notification: add <kbd>padding: 1rem</kbd>, <kbd>border-left: 4px solid coral</kbd>, and <kbd>border-radius: 4px</kbd>.",
|
||||
"task": "This notification needs three things: inner spacing so text isn't cramped, a colored accent on the left edge, and slightly rounded corners.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -172,18 +172,18 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Set <kbd>padding: 1rem</kbd>"
|
||||
"message": "Add inner spacing to the notification"
|
||||
},
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+coral",
|
||||
"message": "Set <kbd>border-left: 4px solid coral</kbd>",
|
||||
"message": "Add a colored accent on the left edge",
|
||||
"options": { "caseSensitive": false }
|
||||
},
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "4px" },
|
||||
"message": "Set <kbd>border-radius: 4px</kbd>"
|
||||
"message": "Soften the corners of the notification"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "colors-1",
|
||||
"title": "Background Color",
|
||||
"description": "Color is one of the most powerful tools in web design. It creates visual hierarchy, conveys meaning, and establishes brand identity. CSS provides multiple ways to specify colors.<br><br><strong>CSS named colors:</strong> CSS includes 147 named colors like <kbd>steelblue</kbd>, <kbd>coral</kbd>, <kbd>gold</kbd>, and <kbd>tomato</kbd>. These are easy to remember and read.<br><br><strong>The background-color property:</strong> Sets the fill color behind an element's content and padding areas.<br><br><pre>.card {\n background-color: lightblue;\n}</pre>",
|
||||
"task": "This notification card needs a subtle background. Add <kbd>background-color: seashell</kbd>.",
|
||||
"task": "This notification card looks bare. Give it a soft, warm background color.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { padding: 1rem; border-left: 4px solid coral; border-radius: 4px; } .alert strong { display: block; margin-bottom: 4px; } .alert p { margin: 0; color: #666; font-size: 0.9rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -20,9 +20,10 @@
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "background-color", "expected": "seashell" },
|
||||
"message": "Set <kbd>background-color: seashell</kbd>"
|
||||
"type": "regex",
|
||||
"value": "background-color:\\s*(seashell|linen|mistyrose|lavenderblush|cornsilk|oldlace|papayawhip|antiquewhite|bisque|peachpuff)",
|
||||
"message": "Which property fills the area behind the content? Try a warm, soft color name",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +31,7 @@
|
||||
"id": "colors-2",
|
||||
"title": "Text Color",
|
||||
"description": "The <kbd>color</kbd> property sets the color of text content. Good contrast between text and background is essential for readability and accessibility.",
|
||||
"task": "Make the alert title stand out. Add <kbd>color: coral</kbd>.",
|
||||
"task": "The alert title blends in with the body text. Make it pop with a warm accent color.",
|
||||
"previewHTML": "<div class=\"alert\"><strong class=\"title\">Warning</strong><p>Your session will expire in 5 minutes</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { padding: 1rem; background-color: seashell; border-left: 4px solid coral; border-radius: 4px; } .alert .title { display: block; margin-bottom: 4px; } .alert p { margin: 0; color: #666; font-size: 0.9rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -41,9 +42,10 @@
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "color", "expected": "coral" },
|
||||
"message": "Set <kbd>color: coral</kbd>"
|
||||
"type": "regex",
|
||||
"value": "color:\\s*(coral|tomato|orangered|indianred|salmon|darksalmon)",
|
||||
"message": "Which property changes the text color? Try a warm, vibrant color name",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -51,7 +53,7 @@
|
||||
"id": "colors-3",
|
||||
"title": "Border Color",
|
||||
"description": "Borders can have their own color using <kbd>border-color</kbd>. This is useful when you want to change just the color without redefining the entire border.",
|
||||
"task": "This card needs an accent border. Add <kbd>border-color: coral</kbd>.",
|
||||
"task": "The card border is dull gray. Give it a warm accent color.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Premium Plan</h3><p>Unlimited access to all features</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { padding: 1rem; background: white; border: 4px solid #ddd; border-radius: 8px; } .card h3 { margin: 0 0 8px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -62,9 +64,10 @@
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-color", "expected": "coral" },
|
||||
"message": "Set <kbd>border-color: coral</kbd>"
|
||||
"type": "regex",
|
||||
"value": "border-color:\\s*(coral|tomato|orangered|indianred|salmon|darksalmon|crimson)",
|
||||
"message": "Which property changes just the border's color? Try a warm, vibrant name",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -72,7 +75,7 @@
|
||||
"id": "colors-4",
|
||||
"title": "Hex Colors",
|
||||
"description": "Beyond named colors, CSS supports hex codes (<kbd>#ff6347</kbd>), RGB (<kbd>rgb(255, 99, 71)</kbd>), and HSL (<kbd>hsl(9, 100%, 64%)</kbd>). Hex codes are the most common format in professional projects.",
|
||||
"task": "Set the badge background to gold using its hex code. Add <kbd>background-color: #ffd700</kbd>.",
|
||||
"task": "This badge needs a golden background. Use a hex color code to set it.",
|
||||
"previewHTML": "<span class=\"badge\">NEW</span>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .badge { display: inline-block; padding: 4px 12px; border-radius: 999px; font-size: 0.75rem; font-weight: bold; text-transform: uppercase; color: #333; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -83,9 +86,10 @@
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "background-color", "expected": "#ffd700" },
|
||||
"message": "Set <kbd>background-color: #ffd700</kbd>"
|
||||
"type": "regex",
|
||||
"value": "background-color:\\s*(#ffd700|#ffcc00|#ffc107|#f0c000|gold)",
|
||||
"message": "Use a hex code for background-color — something in the gold/yellow family",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "box-model-1",
|
||||
"title": "Padding",
|
||||
"description": "كل عنصر في CSS هو صندوق بأربع طبقات: المحتوى، الحشو (padding)، الحدود، والهامش. <strong>Padding</strong> يخلق مساحة تنفس بين محتواك وحافة الصندوق.<br><br>بدون padding، يضغط النص بشكل محرج على الحدود. Padding يجعل المحتوى قابلاً للقراءة ومتوازناً بصرياً.<br><br><pre>.card {\n padding: 1rem;\n}</pre>",
|
||||
"task": "بطاقة الملف الشخصي هذه تبدو ضيقة. أضف <kbd>padding: 1rem</kbd> ليكون للنص مجال للتنفس.",
|
||||
"task": "النص داخل بطاقة الملف الشخصي ملتصق بالحواف. امنحه بعض المساحة الداخلية للتنفس.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -22,7 +22,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "اضبط <kbd>padding: 1rem</kbd>"
|
||||
"message": "أي خاصية تضيف مساحة بين المحتوى وحافة العنصر؟"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"id": "box-model-2",
|
||||
"title": "Borders",
|
||||
"description": "الحدود تنشئ حدوداً مرئية حول العناصر. اختصار <kbd>border</kbd> يقبل ثلاث قيم: العرض، النمط، واللون.<br><br>الأنماط الشائعة: <kbd>solid</kbd>، <kbd>dashed</kbd>، <kbd>dotted</kbd>، <kbd>none</kbd>",
|
||||
"task": "أضف لمسة يسارية خفيفة للبطاقة باستخدام <kbd>border-left: 4px solid steelblue</kbd>.",
|
||||
"task": "هذه البطاقة تحتاج خطاً ملوناً كلمسة على حافتها اليسرى.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
|
||||
"message": "اضبط <kbd>border-left: 4px solid steelblue</kbd>",
|
||||
"message": "استخدم الاختصار الذي يحدد حداً على جانب واحد فقط",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -52,7 +52,7 @@
|
||||
"id": "box-model-3",
|
||||
"title": "Margins",
|
||||
"description": "الهوامش تنشئ مساحة <em>خارج</em> العنصر، تفصله عن جيرانه. بينما يدفع padding المحتوى للداخل، الهوامش تدفع العناصر الأخرى بعيداً.",
|
||||
"task": "أضف مساحة بين بطاقتي الملف الشخصي هاتين باستخدام <kbd>margin-bottom: 1rem</kbd> على <kbd>.card</kbd>.",
|
||||
"task": "بطاقتا الملف الشخصي ملتصقتان ببعضهما. أضف مساحة أسفل كل بطاقة للفصل بينهما.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article><article class=\"card\"><h3>Alex Rivera</h3><p>UX Designer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "margin-bottom", "expected": "1rem" },
|
||||
"message": "اضبط <kbd>margin-bottom: 1rem</kbd>"
|
||||
"message": "أي خاصية تدفع العناصر المجاورة بعيداً من الأسفل؟"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"id": "box-model-4",
|
||||
"title": "Box Sizing",
|
||||
"description": "افتراضياً، <kbd>width</kbd> يحدد فقط عرض المحتوى. Padding والحدود تُضاف للمجموع. هذا يسبب مشاكل في التخطيط.<br><br><kbd>box-sizing: border-box</kbd> يشمل padding والحدود في العرض، مما يجعل التحجيم متوقعاً. معظم المطورين يطبقون هذا على جميع العناصر.",
|
||||
"task": "كلا البطاقتين لهما <kbd>width: 200px</kbd>. اليسرى تستخدم التحجيم الافتراضي (content-box)، مما يجعلها أعرض من المتوقع. أصلح البطاقة اليمنى باستخدام <kbd>box-sizing: border-box</kbd>.",
|
||||
"task": "كلا البطاقتين بنفس العرض، لكن اليسرى تتجاوز لأن الحشو والحدود تُضاف فوق العرض. أصلح البطاقة اليمنى لتشمل الحشو والحدود في حجمها.",
|
||||
"previewHTML": "<div class=\"demo\"><article class=\"card\">Content-box</article><article class=\"card fix\">Border-box</article></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "box-sizing", "expected": "border-box" },
|
||||
"message": "اضبط <kbd>box-sizing: border-box</kbd>"
|
||||
"message": "أي وضع تحجيم يشمل padding والحدود في عرض العنصر؟"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,7 +94,7 @@
|
||||
"id": "box-model-5",
|
||||
"title": "Padding Shorthand",
|
||||
"description": "Padding يقبل 1-4 قيم:<br>• قيمة واحدة: جميع الجوانب<br>• قيمتان: عمودي | أفقي<br>• 4 قيم: أعلى | يمين | أسفل | يسار",
|
||||
"task": "هذا الزر يحتاج مساحة أفقية أكثر من العمودية. اضبط <kbd>padding: 8px 1rem</kbd> (8px أعلى/أسفل، 1rem يسار/يمين).",
|
||||
"task": "هذا الزر ضيق جداً. امنحه مساحة على الجوانب أكثر من الأعلى والأسفل، باستخدام اختصار القيمتين.",
|
||||
"previewHTML": "<button class=\"btn\">Follow</button>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "padding:\\s*8px\\s+1rem",
|
||||
"message": "اضبط <kbd>padding: 8px 1rem</kbd>",
|
||||
"message": "استخدم اختصار القيمتين: العمودي أولاً، ثم الأفقي",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"id": "box-model-6",
|
||||
"title": "Margin Shorthand",
|
||||
"description": "Margin يستخدم نفس نمط الاختصار مثل padding. نمط شائع هو توسيط عناصر الكتلة أفقياً باستخدام <kbd>margin: 0 auto</kbd>.",
|
||||
"task": "وسّط هذه البطاقة أفقياً. اضبط <kbd>margin: 0 auto</kbd> لحساب هوامش يسار/يمين متساوية تلقائياً.",
|
||||
"task": "هذه البطاقة ملتصقة باليسار. وسّطها أفقياً باستخدام اختصار الهوامش مع هوامش جانبية تلقائية.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "margin:\\s*0\\s+auto",
|
||||
"message": "اضبط <kbd>margin: 0 auto</kbd>",
|
||||
"message": "استخدم الاختصار الذي يحسب هوامش أفقية متساوية تلقائياً",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"id": "box-model-7",
|
||||
"title": "Border Radius",
|
||||
"description": "على الرغم من أنه ليس جزءاً من نموذج الصندوق الكلاسيكي، <kbd>border-radius</kbd> يُدوّر زوايا صندوق حدود العنصر. استخدم <kbd>50%</kbd> على عنصر مربع لإنشاء دائرة.",
|
||||
"task": "اجعل صورة الأفاتار دائرية باستخدام <kbd>border-radius: 50%</kbd>.",
|
||||
"task": "صورة الأفاتار المربعة يجب أن تظهر كدائرة مثالية.",
|
||||
"previewHTML": "<article class=\"card\"><img class=\"avatar\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='35' r='25' fill='%23666'/%3E%3Ccircle cx='50' cy='90' r='40' fill='%23666'/%3E%3C/svg%3E\" alt=\"Avatar\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -151,7 +151,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "50%" },
|
||||
"message": "اضبط <kbd>border-radius: 50%</kbd>"
|
||||
"message": "أي خاصية تدوّر الزوايا؟ فكر في النسبة المئوية التي تصنع دائرة"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"id": "box-model-8",
|
||||
"title": "Complete Card",
|
||||
"description": "لنجمع كل شيء معاً. بطاقة الإشعار هذه تحتاج تنسيقاً لتبدو احترافية.",
|
||||
"task": "نسّق الإشعار: أضف <kbd>padding: 1rem</kbd>، <kbd>border-left: 4px solid coral</kbd>، و<kbd>border-radius: 4px</kbd>.",
|
||||
"task": "هذا الإشعار يحتاج ثلاثة أشياء: مساحة داخلية حتى لا يكون النص مزدحماً، لمسة ملونة على الحافة اليسرى، وزوايا مستديرة قليلاً.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -172,18 +172,18 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "اضبط <kbd>padding: 1rem</kbd>"
|
||||
"message": "أضف مساحة داخلية للإشعار"
|
||||
},
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+coral",
|
||||
"message": "اضبط <kbd>border-left: 4px solid coral</kbd>",
|
||||
"message": "أضف لمسة ملونة على الحافة اليسرى",
|
||||
"options": { "caseSensitive": false }
|
||||
},
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "4px" },
|
||||
"message": "اضبط <kbd>border-radius: 4px</kbd>"
|
||||
"message": "نعّم زوايا الإشعار"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "box-model-1",
|
||||
"title": "Padding",
|
||||
"description": "Jedes Element in CSS ist eine Box mit vier Schichten: Inhalt, Padding, Rahmen und Margin. <strong>Padding</strong> schafft Freiraum zwischen deinem Inhalt und dem Rand der Box.<br><br>Ohne Padding drückt sich Text unangenehm gegen Rahmen. Padding macht Inhalte lesbar und visuell ausgewogen.<br><br><pre>.card {\n padding: 1rem;\n}</pre>",
|
||||
"task": "Diese Profilkarte sieht beengt aus. Füge <kbd>padding: 1rem</kbd> hinzu, damit der Text Platz zum Atmen hat.",
|
||||
"task": "Der Text in dieser Profilkarte klebt direkt an den Rändern. Gib ihm etwas inneren Freiraum zum Atmen.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -22,7 +22,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Setze <kbd>padding: 1rem</kbd>"
|
||||
"message": "Welche Eigenschaft fügt Abstand zwischen Inhalt und Elementrand hinzu?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"id": "box-model-2",
|
||||
"title": "Borders",
|
||||
"description": "Rahmen erstellen visuelle Grenzen um Elemente. Die <kbd>border</kbd>-Kurzschreibweise akzeptiert drei Werte: Breite, Stil und Farbe.<br><br>Häufige Stile: <kbd>solid</kbd>, <kbd>dashed</kbd>, <kbd>dotted</kbd>, <kbd>none</kbd>",
|
||||
"task": "Füge der Karte einen dezenten linken Akzent hinzu mit <kbd>border-left: 4px solid steelblue</kbd>.",
|
||||
"task": "Diese Karte könnte eine farbige Akzentlinie an der linken Seite gebrauchen.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
|
||||
"message": "Setze <kbd>border-left: 4px solid steelblue</kbd>",
|
||||
"message": "Verwende die Kurzschreibweise, die einen Rahmen auf nur einer Seite setzt",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -52,7 +52,7 @@
|
||||
"id": "box-model-3",
|
||||
"title": "Margins",
|
||||
"description": "Margins schaffen Abstand <em>außerhalb</em> des Elements und trennen es von Nachbarn. Während Padding den Inhalt nach innen drückt, drücken Margins andere Elemente weg.",
|
||||
"task": "Füge Abstand zwischen diesen beiden Profilkarten hinzu mit <kbd>margin-bottom: 1rem</kbd> auf <kbd>.card</kbd>.",
|
||||
"task": "Diese beiden Profilkarten berühren sich. Füge etwas Abstand unterhalb jeder Karte hinzu, um sie zu trennen.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article><article class=\"card\"><h3>Alex Rivera</h3><p>UX Designer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "margin-bottom", "expected": "1rem" },
|
||||
"message": "Setze <kbd>margin-bottom: 1rem</kbd>"
|
||||
"message": "Welche Eigenschaft schiebt benachbarte Elemente nach unten weg?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"id": "box-model-4",
|
||||
"title": "Box Sizing",
|
||||
"description": "Standardmäßig setzt <kbd>width</kbd> nur die Inhaltsbreite. Padding und Rahmen werden addiert. Das verursacht Layout-Probleme.<br><br><kbd>box-sizing: border-box</kbd> bezieht Padding und Rahmen in die Breite ein, was das Sizing vorhersehbar macht. Die meisten Entwickler wenden dies auf alle Elemente an.",
|
||||
"task": "Beide Karten haben <kbd>width: 200px</kbd>. Die linke nutzt Standard-Sizing (content-box) und wird breiter als erwartet. Korrigiere die rechte Karte mit <kbd>box-sizing: border-box</kbd>.",
|
||||
"task": "Beide Karten haben die gleiche Breite, aber die linke läuft über, weil Padding und Rahmen obendrauf addiert werden. Korrigiere die rechte Karte, damit ihre Größe Padding und Rahmen einschließt.",
|
||||
"previewHTML": "<div class=\"demo\"><article class=\"card\">Content-box</article><article class=\"card fix\">Border-box</article></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "box-sizing", "expected": "border-box" },
|
||||
"message": "Setze <kbd>box-sizing: border-box</kbd>"
|
||||
"message": "Welcher Größenmodus bezieht Padding und Rahmen in die Breite des Elements ein?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,7 +94,7 @@
|
||||
"id": "box-model-5",
|
||||
"title": "Padding Shorthand",
|
||||
"description": "Padding akzeptiert 1-4 Werte:<br>• 1 Wert: alle Seiten<br>• 2 Werte: vertikal | horizontal<br>• 4 Werte: oben | rechts | unten | links",
|
||||
"task": "Dieser Button braucht mehr horizontalen als vertikalen Platz. Setze <kbd>padding: 8px 1rem</kbd> (8px oben/unten, 1rem links/rechts).",
|
||||
"task": "Dieser Button fühlt sich zu eng an. Gib ihm mehr Platz an den Seiten als oben und unten, mit der Zwei-Werte-Kurzschreibweise.",
|
||||
"previewHTML": "<button class=\"btn\">Follow</button>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "padding:\\s*8px\\s+1rem",
|
||||
"message": "Setze <kbd>padding: 8px 1rem</kbd>",
|
||||
"message": "Verwende die Zwei-Werte-Kurzschreibweise: vertikal zuerst, dann horizontal",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"id": "box-model-6",
|
||||
"title": "Margin Shorthand",
|
||||
"description": "Margin nutzt das gleiche Kurzschreibweisen-Muster wie Padding. Ein häufiges Muster ist das horizontale Zentrieren von Block-Elementen mit <kbd>margin: 0 auto</kbd>.",
|
||||
"task": "Zentriere diese Karte horizontal. Setze <kbd>margin: 0 auto</kbd>, um automatisch gleiche links/rechts-Margins zu berechnen.",
|
||||
"task": "Diese Karte klebt links. Zentriere sie horizontal mit der Margin-Kurzschreibweise und automatischen Seitenabständen.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "margin:\\s*0\\s+auto",
|
||||
"message": "Setze <kbd>margin: 0 auto</kbd>",
|
||||
"message": "Verwende die Kurzschreibweise, die gleiche horizontale Abstände automatisch berechnet",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"id": "box-model-7",
|
||||
"title": "Border Radius",
|
||||
"description": "Obwohl nicht Teil des klassischen Box-Modells, rundet <kbd>border-radius</kbd> die Ecken der Rahmen-Box eines Elements. Verwende <kbd>50%</kbd> bei einem quadratischen Element, um einen Kreis zu erstellen.",
|
||||
"task": "Mache das Avatar-Bild rund mit <kbd>border-radius: 50%</kbd>.",
|
||||
"task": "Das quadratische Avatar-Bild soll als perfekter Kreis erscheinen.",
|
||||
"previewHTML": "<article class=\"card\"><img class=\"avatar\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='35' r='25' fill='%23666'/%3E%3Ccircle cx='50' cy='90' r='40' fill='%23666'/%3E%3C/svg%3E\" alt=\"Avatar\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -151,7 +151,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "50%" },
|
||||
"message": "Setze <kbd>border-radius: 50%</kbd>"
|
||||
"message": "Welche Eigenschaft rundet Ecken? Denke daran, welcher Prozentwert einen Kreis ergibt"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"id": "box-model-8",
|
||||
"title": "Complete Card",
|
||||
"description": "Kombinieren wir alles. Diese Benachrichtigungskarte braucht Styling, um professionell auszusehen.",
|
||||
"task": "Style die Benachrichtigung: füge <kbd>padding: 1rem</kbd>, <kbd>border-left: 4px solid coral</kbd> und <kbd>border-radius: 4px</kbd> hinzu.",
|
||||
"task": "Diese Benachrichtigung braucht drei Dinge: inneren Abstand damit der Text nicht gedrängt wirkt, einen farbigen Akzent an der linken Kante und leicht abgerundete Ecken.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -172,18 +172,18 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Setze <kbd>padding: 1rem</kbd>"
|
||||
"message": "Füge inneren Abstand zur Benachrichtigung hinzu"
|
||||
},
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+coral",
|
||||
"message": "Setze <kbd>border-left: 4px solid coral</kbd>",
|
||||
"message": "Füge einen farbigen Akzent an der linken Kante hinzu",
|
||||
"options": { "caseSensitive": false }
|
||||
},
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "4px" },
|
||||
"message": "Setze <kbd>border-radius: 4px</kbd>"
|
||||
"message": "Runde die Ecken der Benachrichtigung ab"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "box-model-1",
|
||||
"title": "Padding",
|
||||
"description": "Cada elemento en CSS es una caja con cuatro capas: contenido, padding, borde y margen. <strong>Padding</strong> crea espacio entre tu contenido y el borde de la caja.<br><br>Sin padding, el texto se aprieta incómodamente contra los bordes. El padding hace que el contenido sea legible y visualmente equilibrado.<br><br><pre>.card {\n padding: 1rem;\n}</pre>",
|
||||
"task": "Esta tarjeta de perfil se ve apretada. Añade <kbd>padding: 1rem</kbd> para que el texto tenga espacio para respirar.",
|
||||
"task": "El texto dentro de esta tarjeta de perfil está pegado a los bordes. Dale algo de espacio interior para respirar.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -22,7 +22,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Establece <kbd>padding: 1rem</kbd>"
|
||||
"message": "¿Qué propiedad añade espacio entre el contenido y el borde del elemento?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"id": "box-model-2",
|
||||
"title": "Borders",
|
||||
"description": "Los bordes crean límites visuales alrededor de los elementos. El atajo <kbd>border</kbd> acepta tres valores: ancho, estilo y color.<br><br>Estilos comunes: <kbd>solid</kbd>, <kbd>dashed</kbd>, <kbd>dotted</kbd>, <kbd>none</kbd>",
|
||||
"task": "Añade un acento sutil a la izquierda de la tarjeta con <kbd>border-left: 4px solid steelblue</kbd>.",
|
||||
"task": "Esta tarjeta necesita una línea de acento de color en su borde izquierdo.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
|
||||
"message": "Establece <kbd>border-left: 4px solid steelblue</kbd>",
|
||||
"message": "Usa el atajo que define un borde en un solo lado",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -52,7 +52,7 @@
|
||||
"id": "box-model-3",
|
||||
"title": "Margins",
|
||||
"description": "Los márgenes crean espacio <em>fuera</em> del elemento, separándolo de sus vecinos. Mientras que el padding empuja el contenido hacia adentro, los márgenes empujan otros elementos hacia afuera.",
|
||||
"task": "Añade espacio entre estas dos tarjetas de perfil con <kbd>margin-bottom: 1rem</kbd> en <kbd>.card</kbd>.",
|
||||
"task": "Estas dos tarjetas de perfil se están tocando. Añade algo de espacio debajo de cada tarjeta para separarlas.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article><article class=\"card\"><h3>Alex Rivera</h3><p>UX Designer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "margin-bottom", "expected": "1rem" },
|
||||
"message": "Establece <kbd>margin-bottom: 1rem</kbd>"
|
||||
"message": "¿Qué propiedad empuja los elementos vecinos hacia abajo?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"id": "box-model-4",
|
||||
"title": "Box Sizing",
|
||||
"description": "Por defecto, <kbd>width</kbd> solo establece el ancho del contenido. Padding y bordes se suman al total. Esto causa problemas de diseño.<br><br><kbd>box-sizing: border-box</kbd> incluye padding y borde en el ancho, haciendo el dimensionamiento predecible. La mayoría de desarrolladores aplican esto a todos los elementos.",
|
||||
"task": "Ambas tarjetas tienen <kbd>width: 200px</kbd>. La izquierda usa el tamaño predeterminado (content-box), haciéndola más ancha de lo esperado. Corrige la tarjeta derecha con <kbd>box-sizing: border-box</kbd>.",
|
||||
"task": "Ambas tarjetas tienen el mismo ancho, pero la izquierda se desborda porque el padding y el borde se suman encima. Corrige la tarjeta derecha para que su tamaño incluya el padding y el borde.",
|
||||
"previewHTML": "<div class=\"demo\"><article class=\"card\">Content-box</article><article class=\"card fix\">Border-box</article></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "box-sizing", "expected": "border-box" },
|
||||
"message": "Establece <kbd>box-sizing: border-box</kbd>"
|
||||
"message": "¿Qué modo de tamaño incluye padding y borde en el ancho del elemento?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,7 +94,7 @@
|
||||
"id": "box-model-5",
|
||||
"title": "Padding Shorthand",
|
||||
"description": "Padding acepta 1-4 valores:<br>• 1 valor: todos los lados<br>• 2 valores: vertical | horizontal<br>• 4 valores: arriba | derecha | abajo | izquierda",
|
||||
"task": "Este botón necesita más espacio horizontal que vertical. Establece <kbd>padding: 8px 1rem</kbd> (8px arriba/abajo, 1rem izquierda/derecha).",
|
||||
"task": "Este botón se siente muy apretado. Dale más espacio en los lados que arriba y abajo, usando el atajo de dos valores.",
|
||||
"previewHTML": "<button class=\"btn\">Follow</button>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "padding:\\s*8px\\s+1rem",
|
||||
"message": "Establece <kbd>padding: 8px 1rem</kbd>",
|
||||
"message": "Usa el atajo de dos valores: vertical primero, luego horizontal",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"id": "box-model-6",
|
||||
"title": "Margin Shorthand",
|
||||
"description": "Margin usa el mismo patrón de atajo que padding. Un patrón común es centrar elementos de bloque horizontalmente con <kbd>margin: 0 auto</kbd>.",
|
||||
"task": "Centra esta tarjeta horizontalmente. Establece <kbd>margin: 0 auto</kbd> para calcular automáticamente márgenes iguales izquierda/derecha.",
|
||||
"task": "Esta tarjeta está pegada a la izquierda. Céntrala horizontalmente usando el atajo de margen con márgenes laterales automáticos.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "margin:\\s*0\\s+auto",
|
||||
"message": "Establece <kbd>margin: 0 auto</kbd>",
|
||||
"message": "Usa el atajo que calcula márgenes horizontales iguales automáticamente",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"id": "box-model-7",
|
||||
"title": "Border Radius",
|
||||
"description": "Aunque no es parte del modelo de caja clásico, <kbd>border-radius</kbd> redondea las esquinas de la caja de borde de un elemento. Usa <kbd>50%</kbd> en un elemento cuadrado para crear un círculo.",
|
||||
"task": "Haz la imagen del avatar circular con <kbd>border-radius: 50%</kbd>.",
|
||||
"task": "La imagen cuadrada del avatar debería aparecer como un círculo perfecto.",
|
||||
"previewHTML": "<article class=\"card\"><img class=\"avatar\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='35' r='25' fill='%23666'/%3E%3Ccircle cx='50' cy='90' r='40' fill='%23666'/%3E%3C/svg%3E\" alt=\"Avatar\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -151,7 +151,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "50%" },
|
||||
"message": "Establece <kbd>border-radius: 50%</kbd>"
|
||||
"message": "¿Qué propiedad redondea las esquinas? Piensa en qué porcentaje crea un círculo"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"id": "box-model-8",
|
||||
"title": "Complete Card",
|
||||
"description": "Combinemos todo. Esta tarjeta de notificación necesita estilo para verse profesional.",
|
||||
"task": "Estiliza la notificación: añade <kbd>padding: 1rem</kbd>, <kbd>border-left: 4px solid coral</kbd> y <kbd>border-radius: 4px</kbd>.",
|
||||
"task": "Esta notificación necesita tres cosas: espacio interior para que el texto no esté apretado, un acento de color en el borde izquierdo y esquinas ligeramente redondeadas.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -172,18 +172,18 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Establece <kbd>padding: 1rem</kbd>"
|
||||
"message": "Añade espacio interior a la notificación"
|
||||
},
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+coral",
|
||||
"message": "Establece <kbd>border-left: 4px solid coral</kbd>",
|
||||
"message": "Añade un acento de color en el borde izquierdo",
|
||||
"options": { "caseSensitive": false }
|
||||
},
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "4px" },
|
||||
"message": "Establece <kbd>border-radius: 4px</kbd>"
|
||||
"message": "Suaviza las esquinas de la notificación"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "box-model-1",
|
||||
"title": "Padding",
|
||||
"description": "Każdy element w CSS to pudełko z czterema warstwami: treść, padding, ramka i margines. <strong>Padding</strong> tworzy przestrzeń między treścią a krawędzią pudełka.<br><br>Bez paddingu tekst przylega niezręcznie do ramek. Padding sprawia, że treść jest czytelna i wizualnie zbalansowana.<br><br><pre>.card {\n padding: 1rem;\n}</pre>",
|
||||
"task": "Ta karta profilu wygląda na ciasną. Dodaj <kbd>padding: 1rem</kbd>, aby tekst miał miejsce do oddychania.",
|
||||
"task": "Tekst wewnątrz tej karty profilu jest przyciśnięty do krawędzi. Daj mu trochę wewnętrznej przestrzeni do oddychania.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -22,7 +22,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Ustaw <kbd>padding: 1rem</kbd>"
|
||||
"message": "Która właściwość dodaje przestrzeń między treścią a krawędzią elementu?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"id": "box-model-2",
|
||||
"title": "Borders",
|
||||
"description": "Ramki tworzą wizualne granice wokół elementów. Skrót <kbd>border</kbd> przyjmuje trzy wartości: szerokość, styl i kolor.<br><br>Popularne style: <kbd>solid</kbd>, <kbd>dashed</kbd>, <kbd>dotted</kbd>, <kbd>none</kbd>",
|
||||
"task": "Dodaj subtelny lewy akcent do karty za pomocą <kbd>border-left: 4px solid steelblue</kbd>.",
|
||||
"task": "Ta karta mogłaby mieć kolorową linię akcentową wzdłuż lewej krawędzi.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
|
||||
"message": "Ustaw <kbd>border-left: 4px solid steelblue</kbd>",
|
||||
"message": "Użyj skrótu, który ustawia ramkę tylko po jednej stronie",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -52,7 +52,7 @@
|
||||
"id": "box-model-3",
|
||||
"title": "Margins",
|
||||
"description": "Marginesy tworzą przestrzeń <em>na zewnątrz</em> elementu, oddzielając go od sąsiadów. Podczas gdy padding przesuwa treść do wewnątrz, marginesy odpychają inne elementy.",
|
||||
"task": "Dodaj przestrzeń między tymi dwiema kartami profilu za pomocą <kbd>margin-bottom: 1rem</kbd> na <kbd>.card</kbd>.",
|
||||
"task": "Te dwie karty profilu stykają się ze sobą. Dodaj trochę przestrzeni pod każdą kartą, aby je rozdzielić.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article><article class=\"card\"><h3>Alex Rivera</h3><p>UX Designer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "margin-bottom", "expected": "1rem" },
|
||||
"message": "Ustaw <kbd>margin-bottom: 1rem</kbd>"
|
||||
"message": "Która właściwość odpycha sąsiednie elementy w dół?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"id": "box-model-4",
|
||||
"title": "Box Sizing",
|
||||
"description": "Domyślnie <kbd>width</kbd> ustawia tylko szerokość treści. Padding i ramki są dodawane. To powoduje problemy z układem.<br><br><kbd>box-sizing: border-box</kbd> włącza padding i ramkę do szerokości, czyniąc rozmiary przewidywalnymi. Większość programistów stosuje to do wszystkich elementów.",
|
||||
"task": "Obie karty mają <kbd>width: 200px</kbd>. Lewa używa domyślnego rozmiaru (content-box), stając się szersza niż oczekiwano. Napraw prawą kartę za pomocą <kbd>box-sizing: border-box</kbd>.",
|
||||
"task": "Obie karty mają tę samą szerokość, ale lewa wychodzi poza, bo padding i ramka są dodawane na wierzch. Napraw prawą kartę, aby jej rozmiar obejmował padding i ramkę.",
|
||||
"previewHTML": "<div class=\"demo\"><article class=\"card\">Content-box</article><article class=\"card fix\">Border-box</article></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "box-sizing", "expected": "border-box" },
|
||||
"message": "Ustaw <kbd>box-sizing: border-box</kbd>"
|
||||
"message": "Który tryb rozmiaru uwzględnia padding i ramkę w szerokości elementu?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,7 +94,7 @@
|
||||
"id": "box-model-5",
|
||||
"title": "Padding Shorthand",
|
||||
"description": "Padding przyjmuje 1-4 wartości:<br>• 1 wartość: wszystkie strony<br>• 2 wartości: pionowo | poziomo<br>• 4 wartości: góra | prawo | dół | lewo",
|
||||
"task": "Ten przycisk potrzebuje więcej miejsca poziomego niż pionowego. Ustaw <kbd>padding: 8px 1rem</kbd> (8px góra/dół, 1rem lewo/prawo).",
|
||||
"task": "Ten przycisk jest zbyt ciasny. Daj mu więcej przestrzeni po bokach niż na górze i dole, używając skrótu dwuwartościowego.",
|
||||
"previewHTML": "<button class=\"btn\">Follow</button>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "padding:\\s*8px\\s+1rem",
|
||||
"message": "Ustaw <kbd>padding: 8px 1rem</kbd>",
|
||||
"message": "Użyj skrótu dwuwartościowego: najpierw pionowo, potem poziomo",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"id": "box-model-6",
|
||||
"title": "Margin Shorthand",
|
||||
"description": "Margines używa tego samego wzorca skrótu co padding. Typowym wzorcem jest poziome centrowanie elementów blokowych za pomocą <kbd>margin: 0 auto</kbd>.",
|
||||
"task": "Wycentruj tę kartę poziomo. Ustaw <kbd>margin: 0 auto</kbd>, aby automatycznie obliczyć równe marginesy lewo/prawo.",
|
||||
"task": "Ta karta jest przyklejona do lewej. Wycentruj ją poziomo, używając skrótu marginesu z automatycznymi marginesami bocznymi.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "margin:\\s*0\\s+auto",
|
||||
"message": "Ustaw <kbd>margin: 0 auto</kbd>",
|
||||
"message": "Użyj skrótu, który automatycznie oblicza równe marginesy poziome",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"id": "box-model-7",
|
||||
"title": "Border Radius",
|
||||
"description": "Chociaż nie jest częścią klasycznego modelu pudełkowego, <kbd>border-radius</kbd> zaokrągla rogi ramki elementu. Użyj <kbd>50%</kbd> na kwadratowym elemencie, aby utworzyć koło.",
|
||||
"task": "Zrób okrągły obrazek awatara za pomocą <kbd>border-radius: 50%</kbd>.",
|
||||
"task": "Kwadratowy obrazek awatara powinien wyglądać jak idealne koło.",
|
||||
"previewHTML": "<article class=\"card\"><img class=\"avatar\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='35' r='25' fill='%23666'/%3E%3Ccircle cx='50' cy='90' r='40' fill='%23666'/%3E%3C/svg%3E\" alt=\"Avatar\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -151,7 +151,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "50%" },
|
||||
"message": "Ustaw <kbd>border-radius: 50%</kbd>"
|
||||
"message": "Która właściwość zaokrągla rogi? Pomyśl, jaki procent tworzy koło"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"id": "box-model-8",
|
||||
"title": "Complete Card",
|
||||
"description": "Połączmy wszystko razem. Ta karta powiadomienia potrzebuje stylowania, żeby wyglądać profesjonalnie.",
|
||||
"task": "Ostyluj powiadomienie: dodaj <kbd>padding: 1rem</kbd>, <kbd>border-left: 4px solid coral</kbd> i <kbd>border-radius: 4px</kbd>.",
|
||||
"task": "To powiadomienie potrzebuje trzech rzeczy: wewnętrznej przestrzeni, żeby tekst nie był ściśnięty, kolorowego akcentu na lewej krawędzi i lekko zaokrąglonych rogów.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -172,18 +172,18 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Ustaw <kbd>padding: 1rem</kbd>"
|
||||
"message": "Dodaj wewnętrzny odstęp do powiadomienia"
|
||||
},
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+coral",
|
||||
"message": "Ustaw <kbd>border-left: 4px solid coral</kbd>",
|
||||
"message": "Dodaj kolorowy akcent na lewej krawędzi",
|
||||
"options": { "caseSensitive": false }
|
||||
},
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "4px" },
|
||||
"message": "Ustaw <kbd>border-radius: 4px</kbd>"
|
||||
"message": "Wygładź rogi powiadomienia"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "box-model-1",
|
||||
"title": "Padding",
|
||||
"description": "Кожен елемент у CSS - це блок з чотирма шарами: контент, відступ (padding), межа та поле. <strong>Padding</strong> створює простір для дихання між вашим контентом і краєм блоку.<br><br>Без padding текст незручно притискається до меж. Padding робить контент читабельним і візуально збалансованим.<br><br><pre>.card {\n padding: 1rem;\n}</pre>",
|
||||
"task": "Ця картка профілю виглядає тісною. Додайте <kbd>padding: 1rem</kbd>, щоб текст мав простір для дихання.",
|
||||
"task": "Текст всередині цієї картки профілю притиснутий до країв. Дайте йому трохи внутрішнього простору для дихання.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -22,7 +22,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Встановіть <kbd>padding: 1rem</kbd>"
|
||||
"message": "Яка властивість додає простір між контентом і краєм елемента?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"id": "box-model-2",
|
||||
"title": "Borders",
|
||||
"description": "Межі створюють візуальні границі навколо елементів. Скорочення <kbd>border</kbd> приймає три значення: ширину, стиль і колір.<br><br>Поширені стилі: <kbd>solid</kbd>, <kbd>dashed</kbd>, <kbd>dotted</kbd>, <kbd>none</kbd>",
|
||||
"task": "Додайте тонкий лівий акцент до картки за допомогою <kbd>border-left: 4px solid steelblue</kbd>.",
|
||||
"task": "Ця картка потребує кольорової акцентної лінії вздовж лівого краю.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+steelblue",
|
||||
"message": "Встановіть <kbd>border-left: 4px solid steelblue</kbd>",
|
||||
"message": "Використайте скорочення, яке встановлює межу лише з одного боку",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -52,7 +52,7 @@
|
||||
"id": "box-model-3",
|
||||
"title": "Margins",
|
||||
"description": "Поля створюють простір <em>зовні</em> елемента, відділяючи його від сусідів. Тоді як padding штовхає контент всередину, поля відштовхують інші елементи.",
|
||||
"task": "Додайте простір між цими двома картками профілю за допомогою <kbd>margin-bottom: 1rem</kbd> на <kbd>.card</kbd>.",
|
||||
"task": "Ці дві картки профілю торкаються одна одної. Додайте трохи простору під кожною карткою, щоб розділити їх.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article><article class=\"card\"><h3>Alex Rivera</h3><p>UX Designer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "margin-bottom", "expected": "1rem" },
|
||||
"message": "Встановіть <kbd>margin-bottom: 1rem</kbd>"
|
||||
"message": "Яка властивість відштовхує сусідні елементи знизу?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"id": "box-model-4",
|
||||
"title": "Box Sizing",
|
||||
"description": "За замовчуванням <kbd>width</kbd> встановлює лише ширину контенту. Padding і межі додаються до загальної суми. Це спричиняє проблеми з макетом.<br><br><kbd>box-sizing: border-box</kbd> включає padding і межу у ширину, роблячи розмір передбачуваним. Більшість розробників застосовують це до всіх елементів.",
|
||||
"task": "Обидві картки мають <kbd>width: 200px</kbd>. Ліва використовує стандартний розмір (content-box), стаючи ширшою за очікуване. Виправте праву картку за допомогою <kbd>box-sizing: border-box</kbd>.",
|
||||
"task": "Обидві картки мають однакову ширину, але ліва виходить за межі, бо відступи та межі додаються зверху. Виправте праву картку, щоб її розмір включав відступи та межі.",
|
||||
"previewHTML": "<div class=\"demo\"><article class=\"card\">Content-box</article><article class=\"card fix\">Border-box</article></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "box-sizing", "expected": "border-box" },
|
||||
"message": "Встановіть <kbd>box-sizing: border-box</kbd>"
|
||||
"message": "Який режим розміру включає padding і межу в ширину елемента?"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -94,7 +94,7 @@
|
||||
"id": "box-model-5",
|
||||
"title": "Padding Shorthand",
|
||||
"description": "Padding приймає 1-4 значення:<br>• 1 значення: всі сторони<br>• 2 значення: вертикально | горизонтально<br>• 4 значення: верх | право | низ | ліво",
|
||||
"task": "Ця кнопка потребує більше горизонтального простору, ніж вертикального. Встановіть <kbd>padding: 8px 1rem</kbd> (8px верх/низ, 1rem ліво/право).",
|
||||
"task": "Ця кнопка занадто тісна. Дайте їй більше простору з боків, ніж зверху та знизу, використовуючи скорочення з двома значеннями.",
|
||||
"previewHTML": "<button class=\"btn\">Follow</button>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "padding:\\s*8px\\s+1rem",
|
||||
"message": "Встановіть <kbd>padding: 8px 1rem</kbd>",
|
||||
"message": "Використайте скорочення з двома значеннями: спочатку вертикальне, потім горизонтальне",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"id": "box-model-6",
|
||||
"title": "Margin Shorthand",
|
||||
"description": "Margin використовує той самий шаблон скорочення, що й padding. Поширений шаблон - горизонтальне центрування блокових елементів за допомогою <kbd>margin: 0 auto</kbd>.",
|
||||
"task": "Відцентруйте цю картку горизонтально. Встановіть <kbd>margin: 0 auto</kbd>, щоб автоматично обчислити рівні ліві/праві поля.",
|
||||
"task": "Ця картка приліпла до лівого краю. Відцентруйте її горизонтально, використовуючи скорочення полів з автоматичними бічними полями.",
|
||||
"previewHTML": "<article class=\"card\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "margin:\\s*0\\s+auto",
|
||||
"message": "Встановіть <kbd>margin: 0 auto</kbd>",
|
||||
"message": "Використайте скорочення, яке автоматично розраховує рівні горизонтальні поля",
|
||||
"options": { "caseSensitive": false }
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"id": "box-model-7",
|
||||
"title": "Border Radius",
|
||||
"description": "Хоча не є частиною класичної блокової моделі, <kbd>border-radius</kbd> заокруглює кути межі елемента. Використовуйте <kbd>50%</kbd> на квадратному елементі, щоб створити коло.",
|
||||
"task": "Зробіть зображення аватара круглим за допомогою <kbd>border-radius: 50%</kbd>.",
|
||||
"task": "Квадратне зображення аватара має виглядати як ідеальне коло.",
|
||||
"previewHTML": "<article class=\"card\"><img class=\"avatar\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='35' r='25' fill='%23666'/%3E%3Ccircle cx='50' cy='90' r='40' fill='%23666'/%3E%3C/svg%3E\" alt=\"Avatar\"><h3>Sarah Chen</h3><p>Frontend Developer</p></article>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -151,7 +151,7 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "50%" },
|
||||
"message": "Встановіть <kbd>border-radius: 50%</kbd>"
|
||||
"message": "Яка властивість заокруглює кути? Подумайте, який відсоток створює коло"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"id": "box-model-8",
|
||||
"title": "Complete Card",
|
||||
"description": "Об'єднаймо все разом. Ця картка сповіщення потребує стилізації, щоб виглядати професійно.",
|
||||
"task": "Стилізуйте сповіщення: додайте <kbd>padding: 1rem</kbd>, <kbd>border-left: 4px solid coral</kbd> та <kbd>border-radius: 4px</kbd>.",
|
||||
"task": "Це сповіщення потребує трьох речей: внутрішнього простору, щоб текст не був стиснутий, кольорового акценту на лівому краю та злегка заокруглених кутів.",
|
||||
"previewHTML": "<div class=\"alert\"><strong>New message</strong><p>You have 3 unread notifications</p></div>",
|
||||
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }",
|
||||
"sandboxCSS": "",
|
||||
@@ -172,18 +172,18 @@
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "padding", "expected": "1rem" },
|
||||
"message": "Встановіть <kbd>padding: 1rem</kbd>"
|
||||
"message": "Додайте внутрішній відступ до сповіщення"
|
||||
},
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "border-left:\\s*4px\\s+solid\\s+coral",
|
||||
"message": "Встановіть <kbd>border-left: 4px solid coral</kbd>",
|
||||
"message": "Додайте кольоровий акцент на лівому краю",
|
||||
"options": { "caseSensitive": false }
|
||||
},
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "border-radius", "expected": "4px" },
|
||||
"message": "Встановіть <kbd>border-radius: 4px</kbd>"
|
||||
"message": "Згладьте кути сповіщення"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
77
specs/004-validation-messages/plan.md
Normal file
77
specs/004-validation-messages/plan.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 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
|
||||
57
specs/004-validation-messages/spec.md
Normal file
57
specs/004-validation-messages/spec.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 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"
|
||||
20
specs/004-validation-messages/tasks.md
Normal file
20
specs/004-validation-messages/tasks.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 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
|
||||
106
specs/009-colors-boxmodel-tasks/plan.md
Normal file
106
specs/009-colors-boxmodel-tasks/plan.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Implementation Plan
|
||||
|
||||
## Objective
|
||||
|
||||
Rewrite task descriptions in the Colors (4 lessons) and Box Model (8 lessons x 6 locales) modules so they describe desired visual outcomes rather than giving exact CSS declarations. For colors, also update validations to accept multiple valid color values.
|
||||
|
||||
## Approach
|
||||
|
||||
This follows the same pattern as the flexbox fix (PR #5). Two types of changes:
|
||||
|
||||
1. **Colors module**: Rewrite tasks AND update validations from `property_value` (single answer) to `regex` (multiple valid colors). This is because the issue explicitly says "accept multiple valid solutions" and colors naturally have many equivalent options.
|
||||
2. **Box Model module**: Rewrite tasks only. Validation messages already use pedagogical hints. Box model properties have specific correct answers (e.g., `box-sizing: border-box` has no alternative), so validations stay as-is.
|
||||
|
||||
## File Mapping
|
||||
|
||||
| File | Action | Description |
|
||||
|------|--------|-------------|
|
||||
| `lessons/03-colors.json` | modify | Rewrite 4 tasks + change 4 validations from `property_value` to `regex` |
|
||||
| `lessons/01-box-model.json` | modify | Rewrite 8 task fields |
|
||||
| `lessons/ar/01-box-model.json` | modify | Rewrite 8 task fields (Arabic) |
|
||||
| `lessons/de/01-box-model.json` | modify | Rewrite 8 task fields (German) |
|
||||
| `lessons/es/01-box-model.json` | modify | Rewrite 8 task fields (Spanish) |
|
||||
| `lessons/pl/01-box-model.json` | modify | Rewrite 8 task fields (Polish) |
|
||||
| `lessons/uk/01-box-model.json` | modify | Rewrite 8 task fields (Ukrainian) |
|
||||
|
||||
No validator code changes needed — existing `regex` type already supports multi-value patterns.
|
||||
|
||||
## Detailed Changes
|
||||
|
||||
### Colors Module
|
||||
|
||||
#### colors-1 (Background Color)
|
||||
- **Task**: Describe that notification card looks bare, needs a soft warm background
|
||||
- **Validation**: Change from `property_value` (seashell only) to `regex` accepting warm named colors (seashell, linen, mistyrose, lavenderblush, cornsilk, oldlace, papayawhip, antiquewhite, bisque, peachpuff)
|
||||
- **Message**: Hint at background-color property
|
||||
|
||||
#### colors-2 (Text Color)
|
||||
- **Task**: Describe that title needs to pop with a warm accent color
|
||||
- **Validation**: Change from `property_value` (coral only) to `regex` accepting warm accent colors (coral, tomato, orangered, indianred, salmon, darksalmon)
|
||||
- **Message**: Hint at color property
|
||||
|
||||
#### colors-3 (Border Color)
|
||||
- **Task**: Describe that card border needs a warm accent color
|
||||
- **Validation**: Change from `property_value` (coral only) to `regex` accepting warm accent colors (coral, tomato, orangered, indianred, salmon, darksalmon, crimson)
|
||||
- **Message**: Hint at border-color property
|
||||
|
||||
#### colors-4 (Hex Colors)
|
||||
- **Task**: Describe wanting a gold/yellow badge background, mentioning hex format since that's the lesson's teaching point
|
||||
- **Validation**: Change from `property_value` (#ffd700 only) to `regex` accepting gold hex variants (#ffd700, #ffcc00, #ffc107, #f0c000) and also the named color `gold`
|
||||
- **Message**: Hint at using a hex code for background-color
|
||||
|
||||
### Box Model Module (per-lesson, applied across all 6 locales)
|
||||
|
||||
#### box-model-1 (Padding)
|
||||
- **Current**: "Add `padding: 1rem`..."
|
||||
- **New**: Describe that text is pressed against the edges and needs inner breathing room
|
||||
|
||||
#### box-model-2 (Borders)
|
||||
- **Current**: "Add `border-left: 4px solid steelblue`"
|
||||
- **New**: Describe wanting a colored accent line on the left side of the card
|
||||
|
||||
#### box-model-3 (Margins)
|
||||
- **Current**: "Add `margin-bottom: 1rem`"
|
||||
- **New**: Describe that the two cards are touching and need space between them
|
||||
|
||||
#### box-model-4 (Box Sizing)
|
||||
- **Current**: "Fix with `box-sizing: border-box`"
|
||||
- **New**: Describe the visual problem (right card overflows) and ask to fix its sizing model
|
||||
|
||||
#### box-model-5 (Padding Shorthand)
|
||||
- **Current**: "Set `padding: 8px 1rem`"
|
||||
- **New**: Describe the button needing more horizontal than vertical space, mention the two-value shorthand concept
|
||||
|
||||
#### box-model-6 (Margin Shorthand)
|
||||
- **Current**: "Set `margin: 0 auto`"
|
||||
- **New**: Describe the card being left-aligned and needing to be horizontally centered
|
||||
|
||||
#### box-model-7 (Border Radius)
|
||||
- **Current**: "Make with `border-radius: 50%`"
|
||||
- **New**: Describe the square avatar needing to appear as a circle
|
||||
|
||||
#### box-model-8 (Complete Card)
|
||||
- **Current**: Lists all 3 exact property declarations
|
||||
- **New**: Describe three visual goals: inner spacing, left accent line, softened corners
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
1. **No validator code changes**: The existing `regex` validation type handles multi-value matching.
|
||||
2. **Colors get multi-value validations**: Colors naturally have equivalents (coral vs tomato). Accept a curated set of named colors per lesson.
|
||||
3. **Box model keeps exact validations**: Properties like `padding: 1rem` or `box-sizing: border-box` have only one correct answer. The task text changes are sufficient.
|
||||
4. **Solution fields unchanged**: The `solution` field shows the canonical answer and is unrelated to copy-paste behavior.
|
||||
5. **codePrefix unchanged**: Already shows the selector context.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Likelihood | Mitigation |
|
||||
|------|-----------|------------|
|
||||
| Color regex too permissive/restrictive | Medium | Curate a small set of 6-10 named colors per lesson that visually work in the preview |
|
||||
| Locale translations lose nuance | Low | Follow the same structure: describe the visual outcome in each language |
|
||||
| Box model tasks become too vague | Low | Keep mentioning the visual problem — students have the description for concept reference |
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
1. Run `npm run test` — all existing tests should pass
|
||||
2. Run `npm run format.lessons` — ensure JSON files are properly formatted
|
||||
3. Verify JSON schema conformance for all modified files
|
||||
50
specs/009-colors-boxmodel-tasks/spec.md
Normal file
50
specs/009-colors-boxmodel-tasks/spec.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# fix: rewrite colors and box-model task descriptions to remove copy-paste answers
|
||||
|
||||
**Issue**: [libretech/code-crispies#9](https://git.librete.ch/libretech/code-crispies/issues/9)
|
||||
**State**: open
|
||||
**Author**: libretech
|
||||
**Labels**: none
|
||||
**Complexity**: medium
|
||||
|
||||
## Issue Body
|
||||
|
||||
Pedagogy audit Runde 3: Colors (copy-paste 90%) and Box Model (copy-paste 85%) are the next worst modules after flexbox was fixed. Same pattern — task says 'Add background-color: coral' and student just types it. Rewrite to describe desired outcome: 'The card background should be a warm color.' Accept multiple valid solutions.
|
||||
|
||||
## Current State
|
||||
|
||||
### Colors Module (`lessons/03-colors.json`) — English only, 4 lessons
|
||||
|
||||
| Lesson | Current Task (gives away answer) |
|
||||
|--------|----------------------------------|
|
||||
| colors-1 | "Add `background-color: seashell`" |
|
||||
| colors-2 | "Add `color: coral`" |
|
||||
| colors-3 | "Add `border-color: coral`" |
|
||||
| colors-4 | "Add `background-color: #ffd700`" |
|
||||
|
||||
All 4 validations use `property_value` with exact expected values — only one answer accepted.
|
||||
|
||||
### Box Model Module (`lessons/01-box-model.json`) — 6 locales (en, ar, de, es, pl, uk), 8 lessons
|
||||
|
||||
| Lesson | Current Task (gives away answer) |
|
||||
|--------|----------------------------------|
|
||||
| box-model-1 | "Add `padding: 1rem`" |
|
||||
| box-model-2 | "Add `border-left: 4px solid steelblue`" |
|
||||
| box-model-3 | "Add `margin-bottom: 1rem`" |
|
||||
| box-model-4 | "Fix with `box-sizing: border-box`" |
|
||||
| box-model-5 | "Set `padding: 8px 1rem`" |
|
||||
| box-model-6 | "Set `margin: 0 auto`" |
|
||||
| box-model-7 | "Make with `border-radius: 50%`" |
|
||||
| box-model-8 | Lists all 3 properties verbatim |
|
||||
|
||||
Box model validation messages are already well-written (hint without revealing). The `task` fields contain `<kbd>` tags with exact answers.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. All 4 colors task descriptions rewritten to describe desired visual outcomes
|
||||
2. All 8 box-model task descriptions rewritten to describe desired visual outcomes
|
||||
3. Students cannot copy-paste from the task into the editor to pass
|
||||
4. Colors validations accept multiple valid CSS color values where appropriate
|
||||
5. Box-model validation messages remain as-is (already hint without revealing)
|
||||
6. All 5 localized box-model files updated to match the English rewrite pattern
|
||||
7. Existing tests continue to pass
|
||||
8. Lesson descriptions (which teach the concepts) remain unchanged
|
||||
22
specs/009-colors-boxmodel-tasks/tasks.md
Normal file
22
specs/009-colors-boxmodel-tasks/tasks.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Tasks
|
||||
|
||||
## Phase 1: Colors Module
|
||||
- [X] Task 1.1: Rewrite task text for all 4 colors lessons to describe visual outcomes
|
||||
- [X] Task 1.2: Change colors validations from property_value to regex accepting multiple valid color names
|
||||
- [X] Task 1.3: Update colors validation error messages to hint without revealing answers
|
||||
|
||||
## Phase 2: Box Model Module (English)
|
||||
- [X] Task 2.1: Rewrite task text for all 8 box-model lessons to describe visual outcomes
|
||||
- [X] Task 2.2: Review box-model validation messages (already good, update only if needed)
|
||||
|
||||
## Phase 3: Box Model Translations [P]
|
||||
- [X] Task 3.1: Rewrite task text in Arabic (ar/01-box-model.json) [P]
|
||||
- [X] Task 3.2: Rewrite task text in German (de/01-box-model.json) [P]
|
||||
- [X] Task 3.3: Rewrite task text in Spanish (es/01-box-model.json) [P]
|
||||
- [X] Task 3.4: Rewrite task text in Polish (pl/01-box-model.json) [P]
|
||||
- [X] Task 3.5: Rewrite task text in Ukrainian (uk/01-box-model.json) [P]
|
||||
|
||||
## Phase 4: Validation
|
||||
- [X] Task 4.1: Run existing test suite to confirm no regressions
|
||||
- [X] Task 4.2: Run lesson format check (npm run format.lessons)
|
||||
- [X] Task 4.3: Verify all modified JSON files conform to module schema
|
||||
Reference in New Issue
Block a user