2025-11-13 08:53:11 +01:00

517 lines
14 KiB
Plaintext

{{define "main-page"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive HTML Tutorial</title>
<script type="module" src="/datastar.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.progress-bar {
background: rgba(255,255,255,0.2);
height: 8px;
border-radius: 4px;
overflow: hidden;
margin-top: 20px;
}
.progress-fill {
background: #4ade80;
height: 100%;
transition: width 0.5s ease;
}
#content {
padding: 40px;
min-height: 400px;
}
h2 {
color: #667eea;
margin-bottom: 20px;
font-size: 2em;
}
h3 {
color: #764ba2;
margin: 20px 0 10px 0;
}
p {
margin-bottom: 15px;
font-size: 1.1em;
}
button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 8px;
font-size: 1em;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin: 10px 10px 10px 0;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.quiz-option {
display: block;
width: 100%;
text-align: left;
margin: 10px 0;
background: #f8f9fa;
color: #333;
}
.quiz-option:hover {
background: #e9ecef;
}
.feedback {
padding: 20px;
border-radius: 10px;
margin: 20px 0;
}
.feedback.success {
background: #d1fae5;
border-left: 4px solid #10b981;
}
.feedback.error {
background: #fee2e2;
border-left: 4px solid #ef4444;
}
.hint {
background: #fef3c7;
border-left: 4px solid #f59e0b;
padding: 15px;
margin: 15px 0;
border-radius: 5px;
}
textarea {
width: 100%;
min-height: 200px;
padding: 15px;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 14px;
margin: 15px 0;
resize: vertical;
}
textarea:focus {
outline: none;
border-color: #667eea;
}
.code-output {
background: #1f2937;
color: #10b981;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
margin: 15px 0;
}
pre {
white-space: pre-wrap;
}
.intro-box {
background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
padding: 30px;
border-radius: 15px;
margin: 20px 0;
}
.stats {
display: flex;
justify-content: space-around;
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
margin: 20px 0;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 2em;
font-weight: bold;
color: #667eea;
}
.stat-label {
color: #6b7280;
font-size: 0.9em;
}
</style>
</head>
<body>
<div id="content" class="container" data-signals='{"progress": 0, "mastery": 0}'>
{{template "intro" .}}
</div>
<script>
// Lightweight click handler to support data-on-click / data-on:click attributes if datastar.js doesn't wire them.
(function(){
function parseSpec(spec) {
spec = (spec||"").trim();
var m = spec.match(/^(?:@|\$+)?([A-Za-z]+)\((['"])(.+?)\2\)$/);
if (!m) return null;
var verb = m[1].toUpperCase();
var path = m[3];
var v2 = path.match(/^([A-Z]+)\s+(\/.*)$/);
if (v2) { verb = v2[1]; path = v2[2]; }
return { method: verb, path: path };
}
document.addEventListener('click', function(e){
// support either attribute name (old and rc6): data-on-click and data-on:click
var el = e.target.closest && e.target.closest('[data-on-click], [data-on\\:click]');
if (!el) return;
var spec = el.getAttribute('data-on-click') || el.getAttribute('data-on:click') || '';
var parsed = parseSpec(spec);
if (!parsed) return;
e.preventDefault();
if (parsed.method === 'GET') {
fetch(parsed.path, { method: 'GET', headers: { Accept: 'text/html' }, credentials: 'same-origin' })
.then(function(res){ return res.text(); })
.then(function(html){
var container = document.getElementById('content');
if (container) {
container.innerHTML = html;
container.querySelectorAll('script').forEach(function(s){
var ns = document.createElement('script');
if (s.src) ns.src = s.src;
ns.text = s.textContent;
document.head.appendChild(ns);
document.head.removeChild(ns);
});
}
})
.catch(function(err){ console.error('nav fetch failed', err); });
return;
}
console.warn('Unhandled method in data-on-click/ data-on:click:', parsed.method);
}, false);
})();
</script>
</body>
</html>
{{end}}
{{define "intro"}}
<div class="intro-box">
<h2>Welcome to Your HTML Journey! 👋</h2>
<p>This interactive tutorial will teach you HTML from the ground up. You'll learn by doing, with:</p>
<ul style="margin: 20px 0 20px 30px; font-size: 1.1em;">
</ul>
<p>The tutorial adapts to your pace - if you're doing great, we'll move faster. If you need more practice, we'll provide it!</p>
<button data-on:click="@get('/node/what-is-html')">
Start Learning
</button>
</div>
{{end}}
{{define "what-is-html"}}
<div>
<h2>What is HTML? 🤔</h2>
<p><strong>HTML</strong> stands for <strong>HyperText Markup Language</strong>. It's the standard language used to create web pages.</p>
<button data-on:click="@get('/node/quiz-what-is-html')">
Next
</button>
</div>
{{end}}
{{define "correct-answer"}}
<div class="feedback success">
<h3>✅ Correct! Great job!</h3>
<p>{{.Explanation}}</p>
<button data-on:click="@get('/node/{{.NextNode}}')">
Continue
</button>
</div>
{{end}}
{{define "wrong-answer"}}
<div class="feedback error">
<h3>❌ Not quite right</h3>
{{if .ShowHint}}
<div class="hint">
</div>
{{end}}
<button data-on:click="@get('/node/quiz-what-is-html')">
Try Again
</button>
</div>
{{end}}
{{define "html-structure"}}
<div>
<h2>HTML Structure 🏗️</h2>
<p>Every HTML document follows a basic structure. Think of it like a house - it needs a foundation, walls, and a roof!</p>
<div class="code-output">
<pre>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Page Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;My First Heading&lt;/h1&gt;
&lt;p&gt;My first paragraph.&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
</div>
<h3>Let's break this down:</h3>
<ul style="margin: 15px 0 15px 30px; line-height: 2;">
<li><code>&lt;!DOCTYPE html&gt;</code> - Tells the browser this is HTML5</li>
<li><code>&lt;html&gt;</code> - The root element, contains everything</li>
<li><code>&lt;head&gt;</code> - Contains metadata (title, links to CSS, etc.)</li>
<li><code>&lt;body&gt;</code> - Contains the visible content</li>s
</ul>
<p>Most HTML elements have an <strong>opening tag</strong> and a <strong>closing tag</strong> (with a /). The content goes between them!</p>
<button data-on-click="@get('/node/html-tags')">
Learn About HTML Tags →
</button>
</div>
{{end}}
{{define "html-tags"}}
<div>
<h2>HTML Tags 🏷️</h2>
<p>HTML uses <strong>tags</strong> to mark up content. Tags tell the browser how to display the content.</p>
<h3>Common HTML Tags:</h3>
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
<p><code>&lt;h1&gt;</code> to <code>&lt;h6&gt;</code> - Headings (h1 is largest)</p>
<p><code>&lt;p&gt;</code> - Paragraph</p>
<p><code>&lt;a&gt;</code> - Link</p>
<p><code>&lt;img&gt;</code> - Image</p>
<p><code>&lt;div&gt;</code> - Division/container</p>
<p><code>&lt;span&gt;</code> - Inline container</p>
</div>
<h3>Tag Anatomy:</h3>
<div class="code-output">
<pre>&lt;p&gt;This is a paragraph&lt;/p&gt;
↑ ↑ ↑
Opening Content Closing
Tag Tag</pre>
</div>
<p>Most tags come in pairs, but some are <strong>self-closing</strong> like <code>&lt;img&gt;</code> and <code>&lt;br&gt;</code>.</p>
<button data-on-click="@get('/node/quiz-html-tags')">
Take the Quiz →
</button>
</div>
{{end}}
{{define "quiz-html-tags"}}
<div>
<h2>Quiz: HTML Tags 📝</h2>
<p>Let's test your knowledge of HTML tags:</p>
<div style="margin: 30px 0;">
<h3>Which tag is used to create a paragraph?</h3>
{{range .QuizOptions}}
<button class="quiz-option" data-on-click="@post('/answer', {quiz: 'quiz-html-tags', answer: '{{.ID}}'})">
{{.Text}}
</button>
{{end}}
</div>
<div id="quiz-feedback"></div>
</div>
{{end}}
{{/* Alias for older/alternate node name used by buttons/routes */}}
{{define "quiz-what-is-html"}}{{template "quiz-html-tags" .}}{{end}}
{{define "exercise-first-tag"}}
<div data-signals='{"code": "{{.StartingCode}}"}'>
<h2>{{.Title}} 💻</h2>
<p>{{.Instructions}}</p>
<p>Remember: HTML tags have an opening tag <code>&lt;h1&gt;</code> and a closing tag <code>&lt;/h1&gt;</code> with the content in between!</p>
<textarea
id="code-editor"
data-model="code"
placeholder="Type your HTML here..."
>{{.StartingCode}}</textarea>
<button data-on-click="@post('/code/check', {exerciseId: '{{.ExerciseID}}', code: $code})">
Check My Code ✓
</button>
<div id="exercise-feedback"></div>
</div>
{{end}}
{{define "html-attributes"}}
<div>
<h2>HTML Attributes 🎯</h2>
<p>Attributes provide additional information about HTML elements. They're always specified in the opening tag.</p>
<div class="code-output">
<pre>&lt;a href="https://example.com"&gt;Click me&lt;/a&gt;
Attribute
(name="value")</pre>
</div>
<h3>Common Attributes:</h3>
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
<p><code>href</code> - Link destination (for <code>&lt;a&gt;</code> tags)</p>
<p><code>src</code> - Image source (for <code>&lt;img&gt;</code> tags)</p>
<p><code>alt</code> - Alternative text for images</p>
<p><code>id</code> - Unique identifier for an element</p>
<p><code>class</code> - Class name for styling</p>
</div>
<h3>Example with Multiple Attributes:</h3>
<div class="code-output">
<pre>&lt;img src="logo.png" alt="Company Logo" width="200"&gt;</pre>
</div>
<button data-on-click="@get('/node/exercise-paragraph')">
Practice with Paragraphs →
</button>
</div>
{{end}}
{{define "exercise-paragraph"}}
<div data-signals='{"code": "{{.StartingCode}}"}'>
<h2>{{.Title}} 💻</h2>
<p>{{.Instructions}}</p>
<p><strong>Tip:</strong> A paragraph tag looks like this: <code>&lt;p&gt;Your text here&lt;/p&gt;</code></p>
<textarea
id="code-editor"
data-model="code"
placeholder="Type your HTML here..."
>{{.StartingCode}}</textarea>
<button data-on-click="@post('/code/check', {exerciseId: '{{.ExerciseID}}', code: $code})">
Check My Code ✓
</button>
<div id="exercise-feedback"></div>
</div>
{{end}}
{{define "advanced-tags"}}
<div>
<h2>Advanced HTML Tags 🚀</h2>
<p>Great job so far! You're ready for more advanced tags. Let's look at lists and semantic HTML.</p>
<h3>Lists:</h3>
<div class="code-output">
<pre>&lt;!-- Unordered List --&gt;
&lt;ul&gt;
&lt;li&gt;Item 1&lt;/li&gt;
&lt;li&gt;Item 2&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- Ordered List --&gt;
&lt;ol&gt;
&lt;li&gt;First&lt;/li&gt;
&lt;li&gt;Second&lt;/li&gt;
&lt;/ol&gt;</pre>
</div>
<h3>Semantic HTML5 Tags:</h3>
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
<p><code>&lt;header&gt;</code> - Page or section header</p>
<p><code>&lt;nav&gt;</code> - Navigation links</p>
<p><code>&lt;main&gt;</code> - Main content</p>
<p><code>&lt;article&gt;</code> - Self-contained content</p>
<p><code>&lt;section&gt;</code> - Thematic grouping</p>
<p><code>&lt;footer&gt;</code> - Page or section footer</p>
</div>
<p>These semantic tags make your HTML more meaningful and accessible!</p>
<button data-on-click="@get('/node/congratulations')">
Complete Tutorial →
</button>
</div>
{{end}}
{{define "congratulations"}}
<div>
<h2>🎉 Congratulations!</h2>
<p>You've completed the HTML Basics tutorial! You now know:</p>
<ul style="margin: 20px 0 20px 30px; font-size: 1.1em; line-height: 2;">
<li>✅ What HTML is and why it's important</li>
<li>✅ Basic HTML structure</li>
<li>✅ Common HTML tags</li>
<li>✅ How to write HTML code</li>
<li>✅ HTML attributes</li>
{{if ge .MasteryScore 80}}
<li>✅ Advanced HTML concepts</li>
{{end}}
</ul>
<div class="stats">
<div class="stat-item">
<div class="stat-value">{{.NodesCompleted}}</div>
<div class="stat-label">Lessons Completed</div>
</div>
<div class="stat-item">
<div class="stat-value">{{.MasteryScore}}%</div>
<div class="stat-label">Mastery Score</div>
</div>
<div class="stat-item">
<div class="stat-value">{{len .CompletedExercises}}</div>
<div class="stat-label">Exercises Done</div>
</div>
</div>
<div class="intro-box">
<h3>What's Next? 🚀</h3>
<p>Continue your web development journey with:</p>
<ul style="margin: 15px 0 15px 30px;">
<li>CSS Tutorial - Style your HTML pages</li>
<li>JavaScript Tutorial - Add interactivity</li>
<li>Build Your First Website - Put it all together</li>
</ul>
</div>
<button data-on-click="@get('/node/intro')">
Start Over
</button>
<button class="btn-secondary" onclick="alert('More tutorials coming soon!')">
Next Tutorial →
</button>
</div>
{{end}}