849 lines
43 KiB
HTML
849 lines
43 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<link rel="icon" href="./favicon.ico" type="image/x-icon" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://librete.ch https://umami.cloud.librete.ch https://liberapay.com; style-src 'self' 'unsafe-inline'; connect-src 'self' https://*.supabase.co wss://*.supabase.co https://umami.cloud.librete.ch; img-src 'self' https://liberapay.com data:; font-src 'self'; frame-src 'self' blob:" />
|
||
|
||
<!-- Umami analytics (self-hosted at umami.cloud.librete.ch) -->
|
||
<script defer src="https://umami.cloud.librete.ch/script.js" data-website-id="4ad21991-5d01-4cf9-9f06-2c0c00ffbab1"></script>
|
||
|
||
<!-- Primary Meta Tags -->
|
||
<title>CODE CRISPIES - Learn HTML & CSS Interactively | Free Coding Practice</title>
|
||
<meta
|
||
name="description"
|
||
content="Master HTML, CSS, and Tailwind through hands-on coding exercises. Free, open-source learning platform with instant feedback. No account required."
|
||
/>
|
||
<meta name="keywords" content="learn CSS, HTML tutorial, Tailwind CSS, web development, coding practice, free" />
|
||
<meta name="robots" content="index, follow" />
|
||
<meta name="theme-color" content="#6366f1" />
|
||
<link rel="canonical" href="https://codecrispi.es/" />
|
||
|
||
<!-- Open Graph / Facebook -->
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:url" content="https://codecrispi.es/" />
|
||
<meta property="og:title" content="CODE CRISPIES - Learn HTML & CSS Interactively" />
|
||
<meta property="og:description" content="Master HTML, CSS, and Tailwind through hands-on coding exercises. Free and open source." />
|
||
<meta property="og:image" content="https://codecrispi.es/og-image.png" />
|
||
<meta property="og:site_name" content="CODE CRISPIES" />
|
||
|
||
<!-- Twitter -->
|
||
<meta name="twitter:card" content="summary_large_image" />
|
||
<meta name="twitter:title" content="CODE CRISPIES - Learn HTML & CSS Interactively" />
|
||
<meta name="twitter:description" content="Master HTML, CSS, and Tailwind through hands-on coding exercises." />
|
||
<meta name="twitter:image" content="https://codecrispi.es/og-image.png" />
|
||
|
||
<!-- Structured Data -->
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context": "https://schema.org",
|
||
"@type": "WebApplication",
|
||
"name": "CODE CRISPIES",
|
||
"description": "Interactive platform for learning HTML, CSS, and Tailwind through hands-on coding exercises",
|
||
"url": "https://codecrispi.es/",
|
||
"applicationCategory": "EducationalApplication",
|
||
"operatingSystem": "Web Browser",
|
||
"offers": {
|
||
"@type": "Offer",
|
||
"price": "0",
|
||
"priceCurrency": "USD"
|
||
},
|
||
"author": {
|
||
"@type": "Organization",
|
||
"name": "LibreTECH",
|
||
"url": "https://librete.ch"
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<link rel="stylesheet" href="main.css" />
|
||
<script defer src="https://librete.ch/u/script.js" data-website-id="2189049f-80c1-4ca0-b0ff-b5cc49276b5f"></script>
|
||
</head>
|
||
<body>
|
||
<a href="#main-content" class="skip-link" data-i18n="skipLink">Skip to main content</a>
|
||
<div class="app-container">
|
||
<header class="header">
|
||
<div class="header-left">
|
||
<button id="menu-btn" class="menu-toggle" data-i18n-aria-label="menuOpen" aria-label="Open menu">
|
||
<span class="hamburger-icon"></span>
|
||
</button>
|
||
<a href="#" class="header-level-pill" id="header-level-pill"></a>
|
||
</div>
|
||
<a href="#" id="logo-link" class="logo">
|
||
<img src="./bowl.png" width="40" alt="CODE CRISPIES Logo" />
|
||
<h1><span class="code-text">CODE</span><span class="crispies-text">CRISPIES</span></h1>
|
||
</a>
|
||
<div class="header-actions">
|
||
<nav class="main-nav" id="main-nav" aria-label="Main sections">
|
||
<a href="#css" class="nav-link" data-section="css">CSS</a>
|
||
<a href="#html" class="nav-link" data-section="html">HTML</a>
|
||
<!-- <a href="#tailwind" class="nav-link" data-section="tailwind">Tailwind</a> -->
|
||
<a href="#markdown" class="nav-link" data-section="markdown">Markdown</a>
|
||
<a href="#javascript" class="nav-link" data-section="javascript">JavaScript</a>
|
||
<a href="#reference/css" class="nav-link nav-link-ref" data-section="reference">Reference</a>
|
||
</nav>
|
||
<button id="auth-trigger-header" class="btn btn-outline btn-sm" data-i18n="authLogin">Log In</button>
|
||
<span id="user-email-header" class="user-email hidden"></span>
|
||
<button id="help-btn" class="help-toggle" data-i18n-aria-label="help" aria-label="Help">?</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Landing Page (Home) -->
|
||
<div class="landing-page hidden" id="landing-page">
|
||
<div class="landing-content">
|
||
<section class="hero">
|
||
<img src="./bowl.png" width="120" alt="" class="hero-logo" />
|
||
<h1>
|
||
<span data-i18n="landingHeroTitle">Learn Web Development</span><br /><span
|
||
class="hero-highlight"
|
||
data-i18n="landingHeroHighlight"
|
||
>By Writing Real Code</span
|
||
>
|
||
</h1>
|
||
<p class="hero-subtitle" data-i18n="landingHeroSubtitle">
|
||
Master HTML, CSS, and Tailwind through hands-on exercises with instant feedback. Free and open source.
|
||
</p>
|
||
<a href="#welcome/0" class="cta-button cta-primary" data-i18n="landingCtaStart">Start Learning NOW</a>
|
||
</section>
|
||
|
||
<section class="why-it-works">
|
||
<h2 data-i18n="landingWhyTitle">Why CODE CRISPIES Works</h2>
|
||
<div class="benefits-grid">
|
||
<article class="benefit-card">
|
||
<svg class="benefit-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="16 18 22 12 16 6"></polyline>
|
||
<polyline points="8 6 2 12 8 18"></polyline>
|
||
</svg>
|
||
<h3 data-i18n="landingBenefit1Title">Learn by Doing</h3>
|
||
<p data-i18n="landingBenefit1Text">
|
||
Write real code from lesson one. No videos to watch—just you, an editor, and instant feedback on every keystroke.
|
||
</p>
|
||
</article>
|
||
<article class="benefit-card">
|
||
<svg class="benefit-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 20V10M18 20V4M6 20v-4"></path>
|
||
</svg>
|
||
<h3 data-i18n="landingBenefit2Title">Practice at Your Pace</h3>
|
||
<p data-i18n="landingBenefit2Text">
|
||
Start with basics, fill gaps in your understanding, then accelerate. Pick up where you left off anytime.
|
||
</p>
|
||
</article>
|
||
<article class="benefit-card">
|
||
<svg class="benefit-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path
|
||
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
|
||
></path>
|
||
</svg>
|
||
<h3 data-i18n="landingBenefit3Title">Master Real Skills</h3>
|
||
<p data-i18n="landingBenefit3Text">
|
||
Learn CSS, HTML, and Tailwind the way professionals use them—through hands-on exercises and reference guides.
|
||
</p>
|
||
</article>
|
||
<article class="benefit-card">
|
||
<svg class="benefit-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||
</svg>
|
||
<h3 data-i18n="landingBenefit4Title">Free & Open Source</h3>
|
||
<p data-i18n="landingBenefit4Text">
|
||
No paywall, no tracking. Optional account for cloud sync across devices. The code is open for everyone.
|
||
</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="learning-paths">
|
||
<h2 data-i18n="landingPathsTitle">Explore Learning Paths</h2>
|
||
<div class="section-cards" id="section-cards">
|
||
<a href="#css" class="section-card" data-section="css">
|
||
<div class="section-card-icon" style="background: #8b6bc4">CSS</div>
|
||
<h3>CSS</h3>
|
||
<p data-i18n="landingCssDesc">Styling, layout, and animations</p>
|
||
<span class="section-card-progress" id="css-progress"></span>
|
||
</a>
|
||
<a href="#html" class="section-card" data-section="html">
|
||
<div class="section-card-icon" style="background: #d4637b">HTML</div>
|
||
<h3>HTML</h3>
|
||
<p data-i18n="landingHtmlDesc">Semantic markup and native elements</p>
|
||
<span class="section-card-progress" id="html-progress"></span>
|
||
</a>
|
||
<!-- Tailwind temporarily disabled
|
||
<a href="#tailwind" class="section-card" data-section="tailwind">
|
||
<div class="section-card-icon" style="background: #26a69a">TW</div>
|
||
<h3>Tailwind CSS</h3>
|
||
<p data-i18n="landingTailwindDesc">Utility-first CSS framework</p>
|
||
<span class="section-card-progress" id="tailwind-progress"></span>
|
||
</a>
|
||
-->
|
||
<a href="#markdown" class="section-card" data-section="markdown">
|
||
<div class="section-card-icon" style="background: #5b8dd9">MD</div>
|
||
<h3>Markdown</h3>
|
||
<p data-i18n="landingMarkdownDesc">Lightweight markup for formatting text</p>
|
||
<span class="section-card-progress" id="markdown-progress"></span>
|
||
</a>
|
||
<a href="#javascript" class="section-card" data-section="javascript">
|
||
<div class="section-card-icon" style="background: #f0db4f; color: #333">JS</div>
|
||
<h3>JavaScript</h3>
|
||
<p data-i18n="landingJsDesc">Interactive scripting for web pages</p>
|
||
<span class="section-card-progress" id="javascript-progress"></span>
|
||
</a>
|
||
</div>
|
||
<p class="device-notice" data-i18n-html="deviceNotice">
|
||
<strong>Best on desktop or tablet (landscape).</strong> Mobile works, but larger screens make coding easier.
|
||
</p>
|
||
</section>
|
||
|
||
<section class="coming-soon">
|
||
<h2 data-i18n="landingComingSoonTitle">Coming Soon</h2>
|
||
<div class="coming-soon-grid">
|
||
<article class="coming-soon-card">
|
||
<span class="coming-soon-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89 17 22l-5-3-5 3 1.523-9.11"/></svg>
|
||
</span>
|
||
<h3 data-i18n="comingSoonAchievementsTitle">Achievements</h3>
|
||
<p data-i18n="comingSoonAchievementsText">Earn badges as you master new skills. Track your learning milestones.</p>
|
||
</article>
|
||
<article class="coming-soon-card">
|
||
<span class="coming-soon-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||
</span>
|
||
<h3 data-i18n="comingSoonFrameworksTitle">Frameworks</h3>
|
||
<p data-i18n="comingSoonFrameworksText">React, Vue, and Svelte basics. Build real components step by step.</p>
|
||
</article>
|
||
<article class="coming-soon-card">
|
||
<span class="coming-soon-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||
</span>
|
||
<h3 data-i18n="comingSoonChallengesTitle">Code Challenges</h3>
|
||
<p data-i18n="comingSoonChallengesText">Test your skills with timed puzzles. Compete on leaderboards and earn ranks.</p>
|
||
</article>
|
||
</div>
|
||
<div class="newsletter-signup">
|
||
<p data-i18n="newsletterText">Want to know when new features launch?</p>
|
||
<form id="newsletter-form" class="newsletter-form">
|
||
<input type="email" id="newsletter-email" required data-i18n-placeholder="newsletterPlaceholder" placeholder="your@email.com">
|
||
<button type="submit" class="btn btn-outline" data-i18n="newsletterButton">Notify Me</button>
|
||
</form>
|
||
<p class="newsletter-disclaimer" data-i18n="newsletterDisclaimer">Max once a week. Unsubscribe anytime via mail@codecrispi.es</p>
|
||
<p id="newsletter-thanks" class="newsletter-thanks hidden" data-i18n="newsletterThanks">Thanks! We'll keep you posted.</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="landing-cta">
|
||
<h2 data-i18n="landingCtaTitle">Start Learning Today</h2>
|
||
<a href="#welcome/0" class="cta-button" data-i18n="landingCtaButton">Let's get crispy!</a>
|
||
<p class="cta-sub" data-i18n="landingCtaSub">Free and open source. No account required. Progress saved locally.</p>
|
||
</section>
|
||
|
||
<footer class="landing-footer">
|
||
<div class="footer-grid">
|
||
<section class="footer-section footer-modules">
|
||
<div id="footer-lesson-links" class="footer-links"></div>
|
||
</section>
|
||
<section class="footer-section">
|
||
<h4 data-i18n="footerResources">Resources</h4>
|
||
<ul class="footer-links">
|
||
<li><a href="#reference/css">CSS Reference</a></li>
|
||
<li><a href="#reference/html">HTML Reference</a></li>
|
||
<li><a href="#playground/0" data-i18n="footerPlayground">Playground</a></li>
|
||
</ul>
|
||
</section>
|
||
<section class="footer-section">
|
||
<h4 data-i18n="footerAbout">About</h4>
|
||
<ul class="footer-links">
|
||
<li><a href="https://librete.ch" target="_blank">LibreTECH</a></li>
|
||
<li><a href="https://git.librete.ch/public/code-crispies" target="_blank">Source Code</a></li>
|
||
<li><a href="https://github.com/nextlevelshit/code-crispies" target="_blank">GitHub</a></li>
|
||
</ul>
|
||
</section>
|
||
<section class="footer-section footer-support">
|
||
<h4 data-i18n="footerSupport">Support</h4>
|
||
<p data-i18n="footerSupportText">Help keep CODE CRISPIES free and open source.</p>
|
||
<script src="https://liberapay.com/libretech/widgets/button.js"></script>
|
||
<noscript><a href="https://liberapay.com/libretech/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||
</section>
|
||
</div>
|
||
<div class="footer-bottom">
|
||
<p>© <span class="current-year"></span> <a href="https://librete.ch">LibreTECH</a>. <span data-i18n="footerLicense">Open source under MIT License.</span></p>
|
||
<p class="footer-legal">
|
||
<button type="button" class="btn-text privacy-link" data-i18n="footerPrivacy">Privacy Policy</button>
|
||
<span class="footer-separator">·</span>
|
||
<button type="button" class="btn-text imprint-link" data-i18n="footerImprint">Imprint</button>
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section Landing Page -->
|
||
<div class="section-page hidden" id="section-page">
|
||
<article class="section-content">
|
||
<header class="section-hero">
|
||
<h1 id="section-title">CSS</h1>
|
||
<p id="section-description">Styling, layout, and animations</p>
|
||
<div class="section-progress-bar">
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="section-progress-fill"></div>
|
||
</div>
|
||
<span class="progress-text" id="section-progress-text">0 of 0 lessons complete</span>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Educational content with integrated module links -->
|
||
<div class="section-intro" id="section-intro"></div>
|
||
</article>
|
||
<footer class="section-footer landing-footer">
|
||
<div class="footer-grid">
|
||
<section class="footer-section footer-modules">
|
||
<div id="section-footer-lesson-links" class="footer-links"></div>
|
||
</section>
|
||
<section class="footer-section">
|
||
<h4 data-i18n="footerResources">Resources</h4>
|
||
<ul class="footer-links">
|
||
<li><a href="#reference/css">CSS Reference</a></li>
|
||
<li><a href="#reference/html">HTML Reference</a></li>
|
||
<li><a href="#playground/0" data-i18n="footerPlayground">Playground</a></li>
|
||
</ul>
|
||
</section>
|
||
<section class="footer-section">
|
||
<h4 data-i18n="footerAbout">About</h4>
|
||
<ul class="footer-links">
|
||
<li><a href="https://librete.ch" target="_blank">LibreTECH</a></li>
|
||
<li><a href="https://git.librete.ch/public/code-crispies" target="_blank">Source Code</a></li>
|
||
<li><a href="https://github.com/nextlevelshit/code-crispies" target="_blank">GitHub</a></li>
|
||
</ul>
|
||
</section>
|
||
<section class="footer-section footer-support">
|
||
<h4 data-i18n="footerSupport">Support</h4>
|
||
<p data-i18n="footerSupportText">Help keep CODE CRISPIES free and open source.</p>
|
||
<script src="https://liberapay.com/libretech/widgets/button.js"></script>
|
||
<noscript><a href="https://liberapay.com/libretech/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||
</section>
|
||
</div>
|
||
<div class="footer-bottom">
|
||
<p>© <span class="current-year"></span> <a href="https://librete.ch">LibreTECH</a>. <span data-i18n="footerLicense">Open source under MIT License.</span></p>
|
||
<p class="footer-legal">
|
||
<button type="button" class="btn-text privacy-link" data-i18n="footerPrivacy">Privacy Policy</button>
|
||
<span class="footer-separator">·</span>
|
||
<button type="button" class="btn-text imprint-link" data-i18n="footerImprint">Imprint</button>
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
|
||
<!-- Reference/Cheatsheet Pages -->
|
||
<div class="reference-page hidden" id="reference-page">
|
||
<article class="reference-content">
|
||
<nav class="reference-nav" id="reference-nav">
|
||
<a href="#reference/css" class="ref-nav-link" data-ref="css">CSS Properties</a>
|
||
<a href="#reference/selectors" class="ref-nav-link" data-ref="selectors">Selectors</a>
|
||
<a href="#reference/flexbox" class="ref-nav-link" data-ref="flexbox">Flexbox</a>
|
||
<a href="#reference/grid" class="ref-nav-link" data-ref="grid">Grid</a>
|
||
<a href="#reference/html" class="ref-nav-link" data-ref="html">HTML Elements</a>
|
||
<a href="#reference/markdown" class="ref-nav-link" data-ref="markdown">Markdown</a>
|
||
</nav>
|
||
<div class="reference-body" id="reference-body">
|
||
<!-- Reference content injected by JS -->
|
||
</div>
|
||
</article>
|
||
<footer class="reference-footer landing-footer">
|
||
<div class="footer-grid">
|
||
<section class="footer-section footer-modules">
|
||
<div id="ref-footer-lesson-links" class="footer-links"></div>
|
||
</section>
|
||
<section class="footer-section">
|
||
<h4 data-i18n="footerResources">Resources</h4>
|
||
<ul class="footer-links">
|
||
<li><a href="#reference/css">CSS Reference</a></li>
|
||
<li><a href="#reference/html">HTML Reference</a></li>
|
||
<li><a href="#playground/0" data-i18n="footerPlayground">Playground</a></li>
|
||
</ul>
|
||
</section>
|
||
<section class="footer-section">
|
||
<h4 data-i18n="footerAbout">About</h4>
|
||
<ul class="footer-links">
|
||
<li><a href="https://librete.ch" target="_blank">LibreTECH</a></li>
|
||
<li><a href="https://git.librete.ch/public/code-crispies" target="_blank">Source Code</a></li>
|
||
<li><a href="https://github.com/nextlevelshit/code-crispies" target="_blank">GitHub</a></li>
|
||
</ul>
|
||
</section>
|
||
<section class="footer-section footer-support">
|
||
<h4 data-i18n="footerSupport">Support</h4>
|
||
<p data-i18n="footerSupportText">Help keep CODE CRISPIES free and open source.</p>
|
||
<script src="https://liberapay.com/libretech/widgets/button.js"></script>
|
||
<noscript><a href="https://liberapay.com/libretech/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||
</section>
|
||
</div>
|
||
<div class="footer-bottom">
|
||
<p>© <span class="current-year"></span> <a href="https://librete.ch">LibreTECH</a>. <span data-i18n="footerLicense">Open source under MIT License.</span></p>
|
||
<p class="footer-legal">
|
||
<button type="button" class="btn-text privacy-link" data-i18n="footerPrivacy">Privacy Policy</button>
|
||
<span class="footer-separator">·</span>
|
||
<button type="button" class="btn-text imprint-link" data-i18n="footerImprint">Imprint</button>
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
|
||
<main class="game-layout" id="main-content">
|
||
<!-- Left Panel: Instructions + Editor -->
|
||
<div class="left-panel">
|
||
<section class="instructions">
|
||
<div class="lesson-title-row">
|
||
<h2 id="lesson-title"></h2>
|
||
<button id="share-btn" class="share-btn" data-i18n-title="shareTitle" title="Share lesson" aria-label="Share lesson">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="task-instruction" id="task-instruction"></div>
|
||
<div class="lesson-description" id="lesson-description"></div>
|
||
</section>
|
||
|
||
<section class="editor-section">
|
||
<div class="code-editor">
|
||
<div class="editor-header">
|
||
<label for="code-input" class="editor-label" data-i18n="editorLabel">CSS Editor</label>
|
||
<div class="editor-actions">
|
||
<div class="editor-tools">
|
||
<button id="random-template-btn" class="btn btn-icon hidden" title="Load random template"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="3" ry="3"/><circle cx="7" cy="7" r="1.5" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none"/><circle cx="17" cy="17" r="1.5" fill="currentColor" stroke="none"/><circle cx="17" cy="7" r="1.5" fill="currentColor" stroke="none"/><circle cx="7" cy="17" r="1.5" fill="currentColor" stroke="none"/></svg></button>
|
||
<button id="undo-btn" class="btn btn-icon" data-i18n-title="undoTitle" title="Undo (Ctrl+Z)">↶</button>
|
||
<button id="redo-btn" class="btn btn-icon" data-i18n-title="redoTitle" title="Redo (Ctrl+Shift+Z)">↷</button>
|
||
<button
|
||
id="reset-code-btn"
|
||
class="btn btn-icon"
|
||
data-i18n-title="resetCodeTitle"
|
||
title="Reset to initial code"
|
||
>
|
||
⟲
|
||
</button>
|
||
</div>
|
||
<button id="run-btn" class="btn btn-run"><img src="./gear.svg" alt="" /><span data-i18n="run">Run</span></button>
|
||
</div>
|
||
</div>
|
||
<div class="editor-content">
|
||
<textarea id="code-input" class="code-input" spellcheck="false" autocomplete="off" style="display: none"></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="hint-area" id="hint-area"></div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- Right Panel: Preview + Navigation -->
|
||
<div class="right-panel">
|
||
<div class="game-controls">
|
||
<button id="prev-btn" class="btn" data-i18n="previous">Previous</button>
|
||
<span class="module-pill" id="module-pill">
|
||
<span class="module-name"></span>
|
||
<span class="level-indicator" id="level-indicator"></span>
|
||
</span>
|
||
<button id="next-btn" class="btn btn-primary" data-i18n="next">Next</button>
|
||
</div>
|
||
<div class="preview-section">
|
||
<div class="preview-wrapper">
|
||
<div class="preview-frame" id="preview-area"></div>
|
||
<div class="expected-overlay" id="expected-overlay">
|
||
<div class="expected-frame" id="preview-expected"></div>
|
||
</div>
|
||
</div>
|
||
<div class="preview-header">
|
||
<span class="preview-label" data-i18n="yourOutput">Your Output</span>
|
||
<button id="show-expected-btn" class="btn btn-small" data-i18n="showExpected">Show Expected</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- Sidebar Backdrop -->
|
||
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
||
|
||
<!-- Slide-out Sidebar -->
|
||
<aside class="sidebar-drawer" id="sidebar-drawer" data-i18n-aria-label="menu" aria-label="Menu">
|
||
<div class="sidebar-header">
|
||
<h3 data-i18n="menu">Menu</h3>
|
||
<button id="close-sidebar" class="close-btn" data-i18n-aria-label="closeMenu" aria-label="Close menu">×</button>
|
||
</div>
|
||
|
||
<nav class="sidebar-section sidebar-nav-mobile" aria-label="Learning paths">
|
||
<a href="#css" class="sidebar-nav-link" data-section="css">CSS</a>
|
||
<a href="#html" class="sidebar-nav-link" data-section="html">HTML</a>
|
||
<!-- <a href="#tailwind" class="sidebar-nav-link" data-section="tailwind">Tailwind</a> -->
|
||
<a href="#javascript" class="sidebar-nav-link" data-section="javascript">JavaScript</a>
|
||
<button id="auth-trigger-mobile" class="sidebar-nav-link sidebar-auth-link" data-i18n="authLogin">Log In</button>
|
||
</nav>
|
||
|
||
<div class="sidebar-section">
|
||
<h4 data-i18n="progress">Progress</h4>
|
||
<div class="progress-display milestone-progress" id="progress-display">
|
||
<div class="milestones" id="milestones">
|
||
<span class="milestone" data-value="1">1</span>
|
||
<span class="milestone" data-value="5">5</span>
|
||
<span class="milestone" data-value="10">10</span>
|
||
<span class="milestone" data-value="20">20</span>
|
||
<span class="milestone" data-value="30">30</span>
|
||
<span class="milestone" data-value="50">50</span>
|
||
<span class="milestone" data-value="75">75</span>
|
||
<span class="milestone" data-value="100">100</span>
|
||
</div>
|
||
<div class="progress-bar-row">
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="progress-fill"></div>
|
||
</div>
|
||
<span class="progress-current" id="progress-current">0/1</span>
|
||
</div>
|
||
<span class="progress-total" id="progress-total">0 of 100 lessons</span>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="sidebar-section" aria-label="Lesson navigation">
|
||
<h4 id="lessons-heading" data-i18n="lessons">Lessons</h4>
|
||
<div class="module-list" id="module-list" role="tree" aria-labelledby="lessons-heading"></div>
|
||
|
||
<div class="sidebar-auth-box">
|
||
<h4 data-i18n="authAccount">Account</h4>
|
||
<button id="auth-trigger-sidebar" class="btn btn-outline btn-full" data-i18n="authLogin">Log In</button>
|
||
<div id="user-menu-sidebar" class="user-menu-sidebar hidden">
|
||
<span id="user-email-sidebar" class="user-email"></span>
|
||
<button id="logout-btn-sidebar" class="btn btn-outline btn-full" data-i18n="authLogout">Log Out</button>
|
||
<button id="delete-account-btn" class="btn btn-text btn-danger btn-full" data-i18n="authDeleteAccount">Delete Account</button>
|
||
</div>
|
||
<p class="sidebar-auth-hint" data-i18n="authSyncHint">Log in to sync progress across devices</p>
|
||
</div>
|
||
</nav>
|
||
|
||
<div class="sidebar-section">
|
||
<h4 data-i18n="settings">Settings</h4>
|
||
<div class="settings-card">
|
||
<label class="settings-row">
|
||
<span class="settings-label" data-i18n="language">Language</span>
|
||
<select id="lang-select" class="lang-select">
|
||
<option value="en">English</option>
|
||
<option value="de">Deutsch</option>
|
||
<option value="pl">Polski</option>
|
||
<option value="es">Español</option>
|
||
<option value="ar">العربية</option>
|
||
<option value="uk">Українська</option>
|
||
</select>
|
||
</label>
|
||
<label class="settings-row">
|
||
<span class="settings-label" data-i18n="showHints">Show Hints</span>
|
||
<input type="checkbox" id="disable-feedback-toggle" class="settings-toggle" checked />
|
||
</label>
|
||
<div class="settings-row">
|
||
<span class="settings-label" data-i18n="resetAllProgress">Reset All Progress</span>
|
||
<button id="reset-btn" class="btn btn-sm btn-ghost" data-i18n="reset">Reset</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="app-footer">
|
||
<span data-i18n="openSource">Open Source:</span>
|
||
<a href="https://git.librete.ch/public/code-crispies" target="_blank">Gitea</a>
|
||
<span data-i18n="by">by</span> <a href="https://librete.ch" title="LibreTECH">LibreTECH</a>
|
||
</footer>
|
||
</aside>
|
||
|
||
<!-- Help Dialog -->
|
||
<dialog id="help-dialog" class="dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="helpTitle">Help</h3>
|
||
<button id="help-dialog-close" class="dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content">
|
||
<h4 data-i18n="aboutTitle">About CODE CRISPIES</h4>
|
||
<p data-i18n="aboutText">
|
||
CODE CRISPIES is a free, open-source platform for learning web development through hands-on exercises. No account required -
|
||
just start coding!
|
||
</p>
|
||
|
||
<h4 data-i18n="learningModesTitle">Learning Modes</h4>
|
||
<ul>
|
||
<li data-i18n-html="modeCss"><strong>CSS</strong> - Write CSS rules to style elements</li>
|
||
<li data-i18n-html="modeTailwind"><strong>Tailwind</strong> - Apply utility classes directly in HTML</li>
|
||
<li data-i18n-html="modeHtml"><strong>HTML</strong> - Practice semantic markup and native elements</li>
|
||
</ul>
|
||
<p class="help-nav-links">
|
||
Jump to: <a href="#css">CSS</a> | <a href="#html">HTML</a> |
|
||
<a href="#reference/css">Reference</a>
|
||
</p>
|
||
|
||
<h4 data-i18n="gettingStartedTitle">Getting Started</h4>
|
||
<p data-i18n="gettingStartedText">
|
||
Open the menu (☰) to browse lesson modules. Each module covers a specific topic with progressive exercises.
|
||
</p>
|
||
|
||
<h4 data-i18n="completingLessonsTitle">Completing Lessons</h4>
|
||
<ol>
|
||
<li data-i18n="completingStep1">Read the task instructions on the left</li>
|
||
<li data-i18n="completingStep2">Write your code in the editor</li>
|
||
<li data-i18n="completingStep3">Watch the live preview update as you type</li>
|
||
<li data-i18n="completingStep4">Follow hints to fix any issues</li>
|
||
<li data-i18n-html="completingStep5">Click <strong>Next</strong> when complete</li>
|
||
</ol>
|
||
|
||
<h4 data-i18n="editorToolsTitle">Editor Tools</h4>
|
||
<ul>
|
||
<li data-i18n-html="editorToolUndo"><strong>↶ Undo</strong> / <strong>↷ Redo</strong> - Navigate edit history</li>
|
||
<li data-i18n-html="editorToolReset"><strong>⟲ Reset</strong> - Restore initial code for current lesson</li>
|
||
<li data-i18n-html="editorToolExpected"><strong>Show Expected</strong> - Toggle the target result overlay</li>
|
||
</ul>
|
||
|
||
<h4 data-i18n="keyboardShortcutsTitle">Keyboard Shortcuts</h4>
|
||
<ul>
|
||
<li data-i18n-html="shortcutUndo"><kbd>Ctrl+Z</kbd> - Undo</li>
|
||
<li data-i18n-html="shortcutRedo"><kbd>Ctrl+Shift+Z</kbd> - Redo</li>
|
||
</ul>
|
||
|
||
<h4 data-i18n="emmetTitle">Emmet Shortcuts (HTML mode)</h4>
|
||
<p data-i18n-html="emmetText">Type abbreviations and press <kbd>Tab</kbd> to expand:</p>
|
||
<ul>
|
||
<li data-i18n-html="emmetClass"><kbd>div.box</kbd> → div with class</li>
|
||
<li data-i18n-html="emmetChildren"><kbd>ul>li*3</kbd> → ul with 3 li children</li>
|
||
<li data-i18n-html="emmetNested"><kbd>form>input+button</kbd> → nested structure</li>
|
||
<li data-i18n-html="emmetContent"><kbd>p{Hello}</kbd> → p with text content</li>
|
||
</ul>
|
||
|
||
<h4 data-i18n="moreProjectsTitle">More Projects</h4>
|
||
<div class="project-cards">
|
||
<a href="https://nextlevelshit.github.io/html-over-js/" target="_blank" class="project-card">
|
||
<strong>HTML over JS</strong>
|
||
<span data-i18n="htmlOverJsDesc"> - Learn to leverage native HTML elements instead of custom JavaScript solutions</span>
|
||
</a>
|
||
<a href="https://nextlevelshit.github.io/web-engineering-mandala/" target="_blank" class="project-card">
|
||
<strong>Web Engineering Mandala</strong>
|
||
<span data-i18n="mandalaDesc"> - Interactive visualization of JavaScript technologies organized by complexity</span>
|
||
</a>
|
||
</div>
|
||
|
||
<h4 data-i18n="contactTitle">Contact & Links</h4>
|
||
<p data-i18n-html="contactText">CODE CRISPIES is developed by <a href="https://librete.ch" target="_blank">LibreTECH</a></p>
|
||
<ul>
|
||
<li><a href="https://git.librete.ch/public/code-crispies" target="_blank">Gitea</a> – Self-hosted source repository</li>
|
||
<li><a href="https://github.com/nextlevelshit/code-crispies" target="_blank">GitHub</a> – Public mirror</li>
|
||
<li><a href="https://www.linkedin.com/in/michael-werner-czechowski" target="_blank">LinkedIn</a> – Michael Czechowski</li>
|
||
</ul>
|
||
|
||
<h4 data-i18n="supportTitle">Support the Project</h4>
|
||
<p data-i18n="supportText">Help keep CODE CRISPIES free and open source.</p>
|
||
<div class="help-support" onclick="typeof umami !== 'undefined' && umami.track('support_click', {location: 'help'})">
|
||
<script src="https://liberapay.com/libretech/widgets/button.js"></script>
|
||
<noscript><a href="https://liberapay.com/libretech/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Reset Code Confirmation Dialog -->
|
||
<dialog id="reset-code-dialog" class="dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="resetCodeDialogTitle">Reset Code</h3>
|
||
<button id="reset-code-dialog-close" class="dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content">
|
||
<p data-i18n="resetCodeDialogText">Reset your code to the initial state for this lesson?</p>
|
||
<label class="toggle-switch" style="margin: 1rem 0">
|
||
<input type="checkbox" id="reset-code-dont-show" />
|
||
<span class="toggle-slider"></span>
|
||
<span class="toggle-label" data-i18n="dontShowAgain">Don't show this again</span>
|
||
</label>
|
||
<div class="dialog-actions">
|
||
<button id="cancel-reset-code" class="btn" data-i18n="cancel">Cancel</button>
|
||
<button id="confirm-reset-code" class="btn btn-ghost" data-i18n="reset">Reset</button>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Reset Confirmation Dialog -->
|
||
<dialog id="reset-dialog" class="dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="resetDialogTitle">Reset Progress</h3>
|
||
<button id="reset-dialog-close" class="dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content">
|
||
<p data-i18n="resetDialogText">Are you sure you want to reset all your progress? This cannot be undone.</p>
|
||
<div class="dialog-actions">
|
||
<button id="cancel-reset" class="btn" data-i18n="cancel">Cancel</button>
|
||
<button id="confirm-reset" class="btn btn-ghost" data-i18n="resetAll">Reset All</button>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Delete Account Confirmation Dialog -->
|
||
<dialog id="delete-account-dialog" class="dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="authDeleteDialogTitle">Delete Account</h3>
|
||
<button id="delete-dialog-close" class="dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content">
|
||
<p data-i18n="authDeleteDialogText">Are you sure you want to delete your account? All your cloud progress will be permanently deleted. This cannot be undone.</p>
|
||
<p id="delete-account-error" class="auth-error hidden"></p>
|
||
<div class="dialog-actions">
|
||
<button id="cancel-delete" class="btn" data-i18n="cancel">Cancel</button>
|
||
<button id="confirm-delete" class="btn btn-danger" data-i18n="authDeleteConfirm">Delete Account</button>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Share Dialog -->
|
||
<dialog id="share-dialog" class="dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="shareDialogTitle">Share Lesson</h3>
|
||
<button id="share-dialog-close" class="dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content">
|
||
<p data-i18n="shareDialogText">Copy this URL to share the current lesson:</p>
|
||
<div class="share-url-container">
|
||
<input type="text" id="share-url-input" class="share-url-input" readonly />
|
||
<button id="copy-url-btn" class="btn btn-primary" data-i18n="copyUrl">Copy</button>
|
||
</div>
|
||
<p id="copy-feedback" class="copy-feedback" data-i18n="urlCopied" hidden>URL copied to clipboard!</p>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Privacy Policy Dialog -->
|
||
<dialog id="privacy-dialog" class="dialog legal-dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="privacyTitle">Privacy Policy</h3>
|
||
<button class="dialog-close privacy-dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content legal-content">
|
||
<p data-i18n="privacyIntro">CODE CRISPIES respects your privacy. This policy explains what data we collect and how we use it.</p>
|
||
|
||
<h4 data-i18n="privacyLocalTitle">Local Storage</h4>
|
||
<p data-i18n="privacyLocalText">Your learning progress, code, and settings are stored locally in your browser. This data never leaves your device unless you create an account.</p>
|
||
|
||
<h4 data-i18n="privacyAccountTitle">Account Data (Optional)</h4>
|
||
<p data-i18n="privacyAccountText">If you create an account, we store your email address and encrypted password to enable cloud sync. Your progress data is synced to our servers (Supabase) so you can access it across devices.</p>
|
||
|
||
<h4 data-i18n="privacyNewsletterTitle">Newsletter (Optional)</h4>
|
||
<p data-i18n="privacyNewsletterText">If you subscribe to our newsletter, we store your email address to send updates about new features. You can unsubscribe anytime.</p>
|
||
|
||
<h4 data-i18n="privacyNoTrackingTitle">No Tracking</h4>
|
||
<p data-i18n="privacyNoTrackingText">We do not use cookies for tracking, analytics, or advertising. We do not share your data with third parties.</p>
|
||
|
||
<h4 data-i18n="privacyRightsTitle">Your Rights (GDPR)</h4>
|
||
<p data-i18n="privacyRightsText">You can delete your account and all associated data at any time from the sidebar menu. For questions or data requests, contact us at mail@codecrispi.es</p>
|
||
|
||
<p class="legal-updated" data-i18n="privacyUpdated">Last updated: January 2025</p>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Imprint Dialog -->
|
||
<dialog id="imprint-dialog" class="dialog legal-dialog">
|
||
<div class="dialog-header">
|
||
<h3 data-i18n="imprintTitle">Imprint</h3>
|
||
<button class="dialog-close imprint-dialog-close" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content legal-content">
|
||
<h4 data-i18n="imprintResponsibleTitle">Responsible for content</h4>
|
||
<p>
|
||
Michael Czechowski<br>
|
||
Schnellweg 3<br>
|
||
70199 Stuttgart<br>
|
||
Germany
|
||
</p>
|
||
|
||
<h4 data-i18n="imprintContactTitle">Contact</h4>
|
||
<p>
|
||
Email: mail@codecrispi.es<br>
|
||
Website: <a href="https://librete.ch" target="_blank">librete.ch</a>
|
||
</p>
|
||
|
||
<h4 data-i18n="imprintDisclaimerTitle">Disclaimer</h4>
|
||
<p data-i18n="imprintDisclaimerText">CODE CRISPIES is provided "as is" without warranty. We are not liable for any damages arising from the use of this service. External links are provided for convenience; we are not responsible for their content.</p>
|
||
</div>
|
||
</dialog>
|
||
|
||
<!-- Auth Dialog -->
|
||
<dialog id="auth-dialog" class="dialog auth-dialog">
|
||
<div class="dialog-header">
|
||
<h2 id="auth-dialog-title" data-i18n="authLogin">Log In</h2>
|
||
<button class="dialog-close close-dialog" aria-label="Close">×</button>
|
||
</div>
|
||
<div class="dialog-content">
|
||
<!-- Login Form -->
|
||
<form id="login-form" class="auth-form">
|
||
<div class="form-field">
|
||
<label for="login-email" data-i18n="authEmail">Email</label>
|
||
<input type="email" id="login-email" required autocomplete="email">
|
||
</div>
|
||
<div class="form-field">
|
||
<label for="login-password" data-i18n="authPassword">Password</label>
|
||
<input type="password" id="login-password" required minlength="6" autocomplete="current-password">
|
||
</div>
|
||
<p id="login-error" class="auth-error hidden"></p>
|
||
<button type="submit" class="btn btn-primary btn-full" data-i18n="authLogin">Log In</button>
|
||
</form>
|
||
|
||
<!-- Signup Form (hidden by default) -->
|
||
<form id="signup-form" class="auth-form hidden">
|
||
<div class="form-field">
|
||
<label for="signup-email" data-i18n="authEmail">Email</label>
|
||
<input type="email" id="signup-email" required autocomplete="email">
|
||
</div>
|
||
<div class="form-field">
|
||
<label for="signup-password" data-i18n="authPassword">Password</label>
|
||
<input type="password" id="signup-password" required minlength="6" autocomplete="new-password">
|
||
</div>
|
||
<div class="form-field">
|
||
<label for="signup-confirm" data-i18n="authConfirmPassword">Confirm Password</label>
|
||
<input type="password" id="signup-confirm" required minlength="6" autocomplete="new-password">
|
||
</div>
|
||
<p id="signup-error" class="auth-error hidden"></p>
|
||
<p id="signup-success" class="auth-success hidden" data-i18n="authSignupSuccess">Account created! Check your email to confirm.</p>
|
||
<button type="submit" class="btn btn-primary btn-full" data-i18n="authSignUp">Sign Up</button>
|
||
</form>
|
||
|
||
<!-- Password Reset Form (hidden by default) -->
|
||
<form id="reset-form" class="auth-form hidden">
|
||
<p class="auth-instructions" data-i18n="authResetInstructions">Enter your email to receive a password reset link.</p>
|
||
<div class="form-field">
|
||
<label for="reset-email" data-i18n="authEmail">Email</label>
|
||
<input type="email" id="reset-email" required autocomplete="email">
|
||
</div>
|
||
<p id="reset-error" class="auth-error hidden"></p>
|
||
<p id="reset-success" class="auth-success hidden" data-i18n="authResetSent">Check your email for the reset link.</p>
|
||
<button type="submit" class="btn btn-primary btn-full" data-i18n="authSendReset">Send Reset Link</button>
|
||
</form>
|
||
|
||
<!-- Social Login Buttons -->
|
||
<div class="auth-social">
|
||
<div class="auth-divider"><span data-i18n="authOrContinueWith">or continue with</span></div>
|
||
<div class="auth-social-buttons">
|
||
<button type="button" id="google-login" class="btn btn-social">
|
||
<svg class="social-icon" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
||
Google
|
||
</button>
|
||
<button type="button" id="github-login" class="btn btn-social">
|
||
<svg class="social-icon" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" fill="currentColor"/></svg>
|
||
GitHub
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Form switcher links -->
|
||
<div class="auth-links">
|
||
<button type="button" id="show-signup" class="btn-text" data-i18n="authNoAccount">Don't have an account? Sign up</button>
|
||
<button type="button" id="show-login" class="btn-text hidden" data-i18n="authHaveAccount">Already have an account? Log in</button>
|
||
<button type="button" id="show-reset" class="btn-text" data-i18n="authForgotPassword">Forgot password?</button>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
</div>
|
||
|
||
<script type="module" src="app.js"></script>
|
||
</body>
|
||
</html>
|