144 lines
11 KiB
JSON
144 lines
11 KiB
JSON
{
|
||
"$schema": "../schemas/code-crispies-module-schema.json",
|
||
"id": "typography-fonts",
|
||
"title": "Typography",
|
||
"description": "Learn how to control text appearance through font selection, sizing, spacing, and decorative effects.",
|
||
"difficulty": "beginner",
|
||
"lessons": [
|
||
{
|
||
"id": "typography-1",
|
||
"title": "Font Family & Fallbacks",
|
||
"description": "Specify custom fonts and reliable fallback stacks for consistent typography across devices.",
|
||
"task": "Set the <code>font-family</code> of the '.text' element to 'Georgia, serif' with serif fallback.",
|
||
"previewHTML": "<p class=\"text\">This text shows the chosen font family.</p>",
|
||
"previewBaseCSS": "body { padding: 1rem; }",
|
||
"sandboxCSS": "",
|
||
"codePrefix": "/* Set font family */\n.text {",
|
||
"initialCode": "",
|
||
"codeSuffix": "}",
|
||
"previewContainer": "preview-area",
|
||
"concept": {
|
||
"explanation": "Font stacks are comma-separated lists that provide fallback options when fonts aren't available on a user's device. Browsers try each font from left to right until they find one installed locally. 'Georgia' is a web-safe font (pre-installed on most systems), and 'serif' is a generic family keyword that tells the browser to use any serif font if Georgia fails. This progressive fallback ensures text always displays in a readable font, even when custom fonts fail to load or aren't supported.",
|
||
"diagram": "Font Stack Resolution Process\n\nfont-family: Georgia, serif;\n\n1. Try Georgia\n ┌─────────────────┐\n │ Is Georgia │ YES → Use Georgia ✓\n │ installed? │\n └─────────────────┘\n │ NO\n ↓\n2. Try serif (generic)\n ┌─────────────────┐\n │ Use browser's │ → Times, Times New Roman,\n │ default serif │ or similar serif font\n └─────────────────┘\n\nCommon web-safe fonts:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nSerif: Georgia, Times\nSans-serif: Arial, Helvetica, Verdana\nMonospace: Courier, Courier New\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nGeneric families (always available):\nserif, sans-serif, monospace,\ncursive, fantasy, system-ui"
|
||
},
|
||
"validations": [
|
||
{
|
||
"type": "contains",
|
||
"value": "font-family",
|
||
"message": "Use the <kbd>font-family</kbd> property",
|
||
"options": { "caseSensitive": false }
|
||
},
|
||
{
|
||
"type": "regex",
|
||
"value": "Georgia, serif",
|
||
"message": "Include <kbd>Georgia, serif</kbd> in the font stack",
|
||
"options": { "caseSensitive": false }
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "typography-2",
|
||
"title": "Font Size & Line Height",
|
||
"description": "Control text scale and readability by adjusting size and line heights.",
|
||
"task": "Set the heading '.heading' to 1.5rem font-size and 1.5 line-height.",
|
||
"previewHTML": "<h2 class=\"heading\">Readable Heading</h2>",
|
||
"previewBaseCSS": "body { padding: 1rem; }",
|
||
"sandboxCSS": "",
|
||
"codePrefix": "/* Set size and line height */\n.heading {",
|
||
"initialCode": "",
|
||
"codeSuffix": "}",
|
||
"previewContainer": "preview-area",
|
||
"concept": {
|
||
"explanation": "Browsers render text sizes using the rem unit (root em), which is relative to the root element's font size (typically 16px by default). 1.5rem equals 24px on most browsers (16px × 1.5). Line-height controls the vertical space allocated for each line—1.5 means each line gets 1.5× the font size in height (36px total for 24px text). Unitless line-height values are preferred because they scale proportionally when font-size changes, maintaining consistent readability. Optimal line-height for body text is usually 1.4-1.6, while headings work well at 1.1-1.3.",
|
||
"diagram": "How Browsers Calculate Text Spacing\n\nfont-size: 1.5rem (24px) line-height: 1.5\n\nRoot font-size (html): 16px\n ↓\n 16px × 1.5 = 24px font-size\n ↓\n 24px × 1.5 = 36px line-height\n\nVisual spacing:\n\n┌────────────────────────────┐\n│ ↕ 6px (leading above) │\n├────────────────────────────┤\n│ Readable Heading (24px) │ ← Text\n├────────────────────────────┤\n│ ↕ 6px (leading below) │\n└────────────────────────────┘\n Total: 36px (line-height)\n\nLeading = (line-height - font-size) / 2\n = (36px - 24px) / 2 = 6px\n\nCommon line-height values:\n━━━━━━━━━━━━━━━━━━━━━━━━\nBody text: 1.5 - 1.6\nHeadings: 1.1 - 1.3\nSingle-line: 1.0 (tight)\nPoetry/code: 1.8 - 2.0"
|
||
},
|
||
"validations": [
|
||
{ "type": "contains", "value": "font-size", "message": "Use <kbd>font-size</kbd> property", "options": { "caseSensitive": false } },
|
||
{
|
||
"type": "property_value",
|
||
"value": { "property": "font-size", "expected": "1.5rem" },
|
||
"message": "Set font-size to <kbd>1.5rem</kbd>"
|
||
},
|
||
{
|
||
"type": "contains",
|
||
"value": "line-height",
|
||
"message": "Use <kbd>line-height</kbd> property",
|
||
"options": { "caseSensitive": false }
|
||
},
|
||
{
|
||
"type": "property_value",
|
||
"value": { "property": "line-height", "expected": "1.5" },
|
||
"message": "Set line-height to <kbd>1.5</kbd>"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "typography-3",
|
||
"title": "Font Weight & Style",
|
||
"description": "Apply weight and style variations like bold, light, italic to emphasize text.",
|
||
"task": "Make the paragraph '.emphasis' italic and bold using <code>font-style</code> and <code>font-weight</code>.",
|
||
"previewHTML": "<p class=\"emphasis\">This text should stand out.</p>",
|
||
"previewBaseCSS": "body { padding: 1rem; }",
|
||
"sandboxCSS": "",
|
||
"codePrefix": "/* Emphasize text */\n.emphasis {",
|
||
"initialCode": "",
|
||
"codeSuffix": "}",
|
||
"previewContainer": "preview-area",
|
||
"concept": {
|
||
"explanation": "Font files contain multiple variations (weights and styles) of the same typeface. When you set font-weight: bold, the browser looks for a bold variant in the font family; if unavailable, it synthesizes boldness by artificially thickening letter strokes (called \"faux bold\"). Font-style: italic requests the true italic variant—if missing, browsers slant the regular font (\"oblique\" or \"faux italic\"). True variants look better because type designers craft them with proper letter spacing and stroke contrast, while synthesized versions simply distort the regular font mathematically.",
|
||
"diagram": "Font Variations Within a Font Family\n\nFont Family: 'Georgia'\n\n┌──────────────────────────────┐\n│ Georgia-Regular.ttf │ font-weight: 400 (normal)\n│ Georgia-Italic.ttf │ font-style: italic\n│ Georgia-Bold.ttf │ font-weight: 700 (bold)\n│ Georgia-BoldItalic.ttf │ both combined\n└──────────────────────────────┘\n\nFont-weight numeric scale:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n100 Thin (rarely used)\n300 Light\n400 Normal/Regular (default)\n600 Semi-Bold\n700 Bold (keyword: bold)\n900 Black/Heavy\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nTrue vs Synthetic (Faux):\n\nTrue Italic: Custom letterforms\nFaux Italic: Slanted regular ⚠️\n\nTrue Bold: Designed thick strokes\nFaux Bold: Artificially thickened ⚠️"
|
||
},
|
||
"validations": [
|
||
{ "type": "contains", "value": "font-style", "message": "Use <kbd>font-style</kbd> property", "options": { "caseSensitive": false } },
|
||
{
|
||
"type": "property_value",
|
||
"value": { "property": "font-style", "expected": "italic" },
|
||
"message": "Set font-style to <kbd>italic</kbd>"
|
||
},
|
||
{
|
||
"type": "contains",
|
||
"value": "font-weight",
|
||
"message": "Use <kbd>font-weight</kbd> property",
|
||
"options": { "caseSensitive": false }
|
||
},
|
||
{
|
||
"type": "property_value",
|
||
"value": { "property": "font-weight", "expected": "bold" },
|
||
"message": "Set font-weight to <kbd>bold</kbd>"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "typography-4",
|
||
"title": "Text Decoration & Shadow",
|
||
"description": "Add decorative underlines, overlines, line-throughs and subtle shadows to text.",
|
||
"task": "Apply an underline with <code>text-decoration</code> and a light shadow using <code>text-shadow</code> on '.fancy'.",
|
||
"previewHTML": "<p class=\"fancy\">Fancy text effect!</p>",
|
||
"previewBaseCSS": "body { padding: 1rem; } .fancy { font-size: 1.25rem; }",
|
||
"sandboxCSS": "",
|
||
"codePrefix": "/* Decorate text */\n.fancy {",
|
||
"initialCode": "",
|
||
"codeSuffix": "}",
|
||
"previewContainer": "preview-area",
|
||
"concept": {
|
||
"explanation": "Text-decoration draws lines relative to the text baseline using the browser's rendering engine—underlines sit below the baseline, overlines above the cap height, and line-throughs at the middle of the x-height. Text-shadow creates depth by rendering duplicate copies of text offset by X and Y coordinates, with optional blur radius and color. The browser draws shadows behind the original text using a Gaussian blur algorithm. Multiple shadows can be stacked (comma-separated) to create complex effects like glows, outlines, or 3D text, with each shadow rendered in order from bottom to top.",
|
||
"diagram": "Text Decoration & Shadow Rendering\n\ntext-decoration: underline;\n\n Cap Height ─┬─ Overline position\n │\n Mean Line ──┼─ Strike-through\n │\n Baseline ───┼─ Text sits here\n │\n └─ Underline position\n\ntext-shadow: 2px 2px 4px gray;\n │ │ │ └─ Color\n │ │ └───── Blur radius\n │ └───────── Vertical offset\n └───────────── Horizontal offset\n\nShadow rendering layers:\n\n┌──────────────────────────┐\n│ Original text (on top) │ ← Layer 3\n├──────────────────────────┤\n│ ░░Blurred shadow copy░░ │ ← Layer 2\n├──────────────────────────┤\n│ Background │ ← Layer 1\n└──────────────────────────┘\n\nCommon patterns:\n━━━━━━━━━━━━━━━━━━━━━━━━━\nSubtle depth: 1px 1px 2px rgba(0,0,0,0.3)\nGlow effect: 0 0 10px gold\n3D text: 2px 2px 0 black (no blur)"
|
||
},
|
||
"validations": [
|
||
{
|
||
"type": "contains",
|
||
"value": "text-decoration",
|
||
"message": "Use <kbd>text-decoration</kbd> property",
|
||
"options": { "caseSensitive": false }
|
||
},
|
||
{
|
||
"type": "contains",
|
||
"value": "text-shadow",
|
||
"message": "Use <kbd>text-shadow</kbd> property",
|
||
"options": { "caseSensitive": false }
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|