feat: Implement an interactive web-based Python documentation viewer with a code playground.

This commit is contained in:
blshaer 2026-01-03 09:22:55 +02:00
parent f4c488ad93
commit 689752b14c
3 changed files with 977 additions and 0 deletions

127
index.html Normal file
View File

@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Python By Example | Interactive Learning Docs</title>
<!-- SEO Meta Tags -->
<meta name="description"
content="A premium, interactive documentation hub for mastering Python by example. Run code live in your browser.">
<meta name="keywords" content="Python, Tutorial, Learn Python, Interactive Python, Pyodide, Coding, Programming">
<meta name="author" content="blshaer">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://pythonbyexample.github.io/">
<meta property="og:title" content="Python By Example | Interactive Learning Docs">
<meta property="og:description"
content="Master Python with real-world examples and an interactive browser-only playground.">
<meta property="og:image"
content="https://cdn.dribbble.com/userupload/46242920/file/bdbef935da688edfd85fcfeca75754a0.png?resize=1200x675&vertical=center">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://pythonbyexample.github.io/">
<meta property="twitter:title" content="Python By Example | Interactive Learning Docs">
<meta property="twitter:description"
content="Master Python with real-world examples and an interactive browser-only playground.">
<meta property="twitter:image"
content="https://cdn.dribbble.com/userupload/46242920/file/bdbef935da688edfd85fcfeca75754a0.png?resize=1200x675&vertical=center">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Monaco Editor -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js"></script>
<!-- Pyodide Core -->
<script src="https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js"></script>
</head>
<body class="readme-theme">
<div id="playground-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<div class="file-info">
<i class="fab fa-python"></i>
<span id="lesson-title">Playground.py</span>
</div>
<div class="header-actions">
<button id="edit-btn" class="btn-ghost" title="Enable editing"><i class="fas fa-edit"></i>
Edit</button>
<button id="reset-btn" class="btn-ghost" title="Reset to original code"><i class="fas fa-undo"></i>
Reset</button>
<button id="run-btn" class="btn-run" disabled><i class="fas fa-play"></i> Run Code</button>
<button class="close-modal"><i class="fas fa-times"></i></button>
</div>
</div>
<div class="playground-body">
<div class="editor-pane">
<div id="monaco-editor-container" style="width: 100%; height: 100%;"></div>
</div>
<div class="output-pane">
<div class="output-header">Terminal Output</div>
<pre id="output-area">Loading Python environment... (Ready in ~10s)</pre>
</div>
</div>
</div>
</div>
<nav class="top-nav">
<div class="nav-container">
<div class="logo">
<i class="fab fa-python"></i>
<span>Python<span>By Example</span></span>
</div>
<div class="search-box">
<i class="fas fa-search"></i>
<input type="text" placeholder="Search documentation...">
</div>
<div class="ext-links">
<a href="https://github.com/blshaer/python-by-example" target="_blank" title="GitHub Repository"><i
class="fab fa-github"></i></a>
</div>
</div>
</nav>
<div class="app-layout">
<aside class="sidebar">
<div class="sidebar-header">Table of Contents</div>
<nav id="toc" class="toc-nav">
<!-- TOC links will be injected here -->
<div class="skeleton-line"></div>
<div class="skeleton-line"></div>
<div class="skeleton-line"></div>
</nav>
</aside>
<main class="content-area">
<header class="readme-header">
<h1>Python Mastery <span class="version">v1.0.0</span></h1>
<p class="lead">Complete roadmap for mastering Python, structured for clarity and professional growth.
</p>
</header>
<section id="documentation-content">
<!-- Documentation modules will be injected here -->
<div class="skeleton-section"></div>
<div class="skeleton-section"></div>
</section>
</main>
</div>
<footer class="docs-footer">
<div class="footer-inner">
<p>Made with ❤️ for the Python Community.</p>
</div>
</footer>
<script src="script.js"></script>
</body>
</html>

353
script.js Normal file
View File

@ -0,0 +1,353 @@
const documentation = [
{
id: "01_basics",
title: "01. Python Basics",
description: "Fundamental syntax and data types.",
icon: "fas fa-seedling",
lessons: [
{ title: "Print Function", file: "01_basics/01_print.py" },
{ title: "Comments", file: "01_basics/02_comments.py" },
{ title: "Variables", file: "01_basics/03_variables.py" },
{ title: "Data Types", file: "01_basics/04_data_types.py" }
]
},
{
id: "02_control_flow",
title: "02. Control Flow",
description: "Conditional logic and selection.",
icon: "fas fa-code-branch",
lessons: [
{ title: "If-Else", file: "02_control_flow/01_if_else.py" },
{ title: "Elif", file: "02_control_flow/02_elif.py" },
{ title: "Match Case", file: "02_control_flow/03_match_case.py" }
]
},
{
id: "03_loops",
title: "03. Loops & Iterations",
description: "Repetitive task automation.",
icon: "fas fa-sync",
lessons: [
{ title: "For Loops", file: "03_loops/01_for_loop.py" },
{ title: "While Loops", file: "03_loops/02_while_loop.py" },
{ title: "Break & Continue", file: "03_loops/03_break_continue.py" }
]
},
{
id: "04_data_structures",
title: "04. Data Structures",
description: "Collections and data management.",
icon: "fas fa-database",
lessons: [
{ title: "Lists", file: "04_data_structures/01_lists.py" },
{ title: "Tuples", file: "04_data_structures/02_tuples.py" },
{ title: "Sets", file: "04_data_structures/03_sets.py" },
{ title: "Dictionaries", file: "04_data_structures/04_dictionaries.py" }
]
},
{
id: "05_functions",
title: "05. Functions",
description: "Modular and reusable blocks.",
icon: "fas fa-cube",
lessons: [
{ title: "Function Basics", file: "05_functions/01_function_basics.py" },
{ title: "Arguments", file: "05_functions/02_arguments.py" },
{ title: "Return Values", file: "05_functions/03_return_values.py" },
{ title: "Lambda Functions", file: "05_functions/04_lambda_functions.py" }
]
},
{
id: "06_modules_packages",
title: "06. Modules & Packages",
description: "Code organization and imports.",
icon: "fas fa-box-open",
lessons: [
{ title: "Imports", file: "06_modules_packages/01_imports.py" },
{ title: "Custom Modules", file: "06_modules_packages/02_custom_modules.py" }
]
},
{
id: "07_error_handling",
title: "07. Error Handling",
description: "Robustness and exception safety.",
icon: "fas fa-exclamation-triangle",
lessons: [
{ title: "Try-Except", file: "07_error_handling/01_try_except.py" },
{ title: "Custom Exceptions", file: "07_error_handling/02_custom_exceptions.py" }
]
},
{
id: "08_oop",
title: "08. Object Oriented Programming",
description: "Classes, objects, and inheritance.",
icon: "fas fa-project-diagram",
lessons: [
{ title: "Classes & Objects", file: "08_oop/01_classes_objects.py" },
{ title: "Init Methods", file: "08_oop/02_init_methods.py" },
{ title: "Inheritance", file: "08_oop/03_inheritance.py" },
{ title: "Polymorphism", file: "08_oop/04_polymorphism.py" }
]
},
{
id: "09_advanced_python",
title: "09. Advanced Python",
description: "Generators, decorators, and more.",
icon: "fas fa-bolt",
lessons: [
{ title: "List Comprehensions", file: "09_advanced_python/01_list_comprehensions.py" },
{ title: "Generators", file: "09_advanced_python/02_generators.py" },
{ title: "Decorators", file: "09_advanced_python/03_decorators.py" },
{ title: "Context Managers", file: "09_advanced_python/04_context_managers.py" }
]
},
{
id: "10_best_practices",
title: "10. Best Practices",
description: "Clean code and professional PEP 8.",
icon: "fas fa-check-double",
lessons: [
{ title: "PEP 8", file: "10_best_practices/01_pep8.py" },
{ title: "Type Hinting", file: "10_best_practices/02_type_hinting.py" },
{ title: "Virtual Envs", file: "10_best_practices/03_virtual_envs.py" }
]
}
];
let pyodideInstance = null;
let editor = null;
let originalCode = "";
// Initialize Monaco Editor
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' } });
require(['vs/editor/editor.main'], function () {
editor = monaco.editor.create(document.getElementById('monaco-editor-container'), {
value: "# Select a lesson to begin...",
language: 'python',
theme: 'vs-dark',
automaticLayout: true,
readOnly: true,
fontSize: 14,
minimap: { enabled: false },
lineNumbers: 'on',
scrollBeyondLastLine: false,
padding: { top: 20 }
});
});
async function initPyodide() {
const outputArea = document.getElementById('output-area');
try {
pyodideInstance = await loadPyodide();
outputArea.textContent = ">>> Python 3.10 (Pyodide) Ready.\n";
document.getElementById('run-btn').disabled = false;
} catch (err) {
outputArea.textContent = "Error loading Pyodide: " + err.message;
}
}
async function runPython(code) {
const outputArea = document.getElementById('output-area');
if (!pyodideInstance) {
outputArea.textContent = ">>> Error: Python environment not yet initialized. Please wait.\n";
return;
}
outputArea.textContent = "Running...\n";
try {
pyodideInstance.setStdout({
batched: (text) => {
outputArea.textContent += text + "\n";
}
});
await pyodideInstance.runPythonAsync(code);
outputArea.textContent += "\n[Execution Complete]";
} catch (err) {
outputArea.textContent += "\nTraceback (most recent call last):\n" + err.message;
}
}
document.addEventListener('DOMContentLoaded', () => {
initPyodide();
const content = document.getElementById('documentation-content');
const toc = document.getElementById('toc');
const modal = document.getElementById('playground-modal');
const lessonTitle = document.getElementById('lesson-title');
const resetBtn = document.getElementById('reset-btn');
const editBtn = document.getElementById('edit-btn');
const runBtn = document.getElementById('run-btn');
const closeModal = document.querySelector('.close-modal');
content.innerHTML = '';
toc.innerHTML = '';
// Render Documentation
documentation.forEach((module) => {
const section = document.createElement('section');
section.id = module.id;
section.className = 'doc-section';
section.innerHTML = `
<h2><i class="${module.icon || 'fas fa-book'}" style="margin-right: 12px; font-size: 1.2rem; color: var(--accent);"></i>${module.title}</h2>
<p>${module.description}</p>
<div class="module-card">
<ul class="lesson-list" style="list-style: none; padding-left: 0;">
${module.lessons.map(lesson => `
<li class="lesson-item" data-file="${lesson.file}" data-title="${lesson.title}"
style="padding: 12px; border-bottom: 1px solid var(--border-color); display: flex; align-items: center; justify-content: space-between; cursor: pointer; transition: 0.2s; border-radius: 6px;">
<div style="display: flex; align-items: center; gap: 12px;">
<i class="fab fa-python" style="color: var(--accent);"></i>
<span>${lesson.title}</span>
</div>
<span style="font-size: 0.8rem; color: var(--text-secondary);">Run Live <i class="fas fa-play" style="font-size: 0.6rem;"></i></span>
</li>
`).join('')}
</ul>
</div>
`;
content.appendChild(section);
const tocLink = document.createElement('a');
tocLink.href = `#${module.id}`;
tocLink.className = 'toc-link';
tocLink.textContent = module.title;
toc.appendChild(tocLink);
});
// Handle Lesson Clicks
document.addEventListener('click', async (e) => {
const item = e.target.closest('.lesson-item');
if (!item) return;
const file = item.getAttribute('data-file');
const title = item.getAttribute('data-title');
lessonTitle.textContent = `${title}.py`;
modal.classList.add('active');
// Monaco needs layout() called when container becomes visible to display cursor/suggestions correctly
setTimeout(() => {
if (editor) {
editor.layout();
editor.focus();
}
}, 100);
// Reset edit mode
editBtn.classList.remove('active');
editBtn.innerHTML = '<i class="fas fa-edit"></i> Edit';
if (editor) editor.updateOptions({ readOnly: true });
if (editor) editor.setValue("# Loading source code...");
try {
const response = await fetch(file);
if (!response.ok) throw new Error("File not found");
const code = await response.text();
originalCode = code;
if (editor) {
editor.setValue(code);
// Force suggestions/intellisense refresh
monaco.editor.setModelLanguage(editor.getModel(), 'python');
}
} catch (err) {
const fallback = `# Error loading file: ${file}\n# serving from github.io might require valid paths.\n\nprint("Hello from Python By Example!")\nname = "Developer"\nprint(f"Happy coding, {name}!")`;
originalCode = fallback;
if (editor) editor.setValue(fallback);
}
});
// Edit Toggle
editBtn.addEventListener('click', () => {
if (!editor) return;
const isEditing = editBtn.classList.toggle('active');
editor.updateOptions({ readOnly: !isEditing });
if (isEditing) {
editBtn.innerHTML = '<i class="fas fa-check"></i> Editing...';
editor.focus();
document.getElementById('output-area').textContent = ">>> Editing mode enabled. You can now modify the code and see suggestions.\n";
} else {
editBtn.innerHTML = '<i class="fas fa-edit"></i> Edit';
document.getElementById('output-area').textContent = ">>> Editing mode disabled.\n";
}
});
// Reset Logic
resetBtn.addEventListener('click', () => {
if (!editor) return;
editor.setValue(originalCode);
editBtn.classList.remove('active');
editor.updateOptions({ readOnly: true });
editBtn.innerHTML = '<i class="fas fa-edit"></i> Edit';
document.getElementById('output-area').textContent = ">>> Code reset to original state.\n";
});
// Run Logic
runBtn.addEventListener('click', () => {
if (editor) runPython(editor.getValue());
});
// Modal Controls
closeModal.addEventListener('click', () => modal.classList.remove('active'));
modal.addEventListener('click', (e) => {
if (e.target === modal) modal.classList.remove('active');
});
// Search Functionality (Improved)
const searchInput = document.querySelector('.search-box input');
searchInput.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase().trim();
document.querySelectorAll('.doc-section').forEach(section => {
const heading = section.querySelector('h2').textContent.toLowerCase();
const desc = section.querySelector('p').textContent.toLowerCase();
const lessonItems = section.querySelectorAll('.lesson-item');
let isSectionVisible = query === "" || heading.includes(query) || desc.includes(query);
let matchingLessonsCount = 0;
lessonItems.forEach(item => {
const lessonText = item.textContent.toLowerCase();
const isLessonMatch = query === "" || lessonText.includes(query);
// Show lesson if it matches or if the section title matches
const shouldShowLesson = isSectionVisible || isLessonMatch;
item.style.setProperty('display', shouldShowLesson ? 'flex' : 'none', 'important');
if (isLessonMatch) matchingLessonsCount++;
});
// Show section if title matches or if any lesson inside matches
section.style.display = (isSectionVisible || matchingLessonsCount > 0) ? 'block' : 'none';
});
// Sync TOC
document.querySelectorAll('.toc-link').forEach(link => {
const targetId = link.getAttribute('href').substring(1);
const targetSection = document.getElementById(targetId);
if (targetSection) {
link.style.display = targetSection.style.display === 'none' ? 'none' : 'block';
}
});
});
// Scroll Spy
window.addEventListener('scroll', () => {
let current = '';
document.querySelectorAll('.doc-section').forEach(section => {
if (section.style.display !== 'none' && window.pageYOffset >= (section.offsetTop - 150)) {
current = section.getAttribute('id');
}
});
document.querySelectorAll('.toc-link').forEach(link => {
link.classList.remove('active');
if (link.style.display !== 'none' && link.getAttribute('href').substring(1) === current) {
link.classList.add('active');
}
});
});
});

497
style.css Normal file
View File

@ -0,0 +1,497 @@
:root {
--bg-main: #0d1117;
--bg-side: #010409;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent: #58a6ff;
--accent-bg: rgba(56, 139, 253, 0.15);
--code-bg: #161b22;
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
--font-mono: 'Fira Code', ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--bg-main);
color: var(--text-primary);
font-family: var(--font-sans);
line-height: 1.6;
overflow-x: hidden;
}
/* Navigation */
.top-nav {
position: sticky;
top: 0;
background: var(--bg-side);
border-bottom: 1px solid var(--border-color);
padding: 12px 0;
z-index: 1000;
}
.nav-container {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40px;
}
.logo {
font-weight: 700;
font-size: 1.25rem;
display: flex;
align-items: center;
gap: 10px;
}
.logo span span {
color: var(--accent);
}
.search-box {
position: relative;
width: 300px;
}
.search-box i {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
}
.search-box input {
width: 100%;
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 8px 12px 8px 36px;
color: var(--text-primary);
outline: none;
transition: 0.2s;
}
.search-box input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-bg);
}
/* Layout */
.app-layout {
display: flex;
max-width: 1400px;
margin: 40px auto;
padding: 0 40px;
}
.sidebar {
width: 280px;
position: sticky;
top: 100px;
height: calc(100vh - 120px);
overflow-y: auto;
padding-right: 20px;
}
.sidebar-header {
font-size: 0.8rem;
text-transform: uppercase;
color: var(--text-secondary);
letter-spacing: 0.05em;
font-weight: 700;
margin-bottom: 16px;
}
.toc-nav {
display: flex;
flex-direction: column;
gap: 8px;
}
.toc-link {
text-decoration: none;
color: var(--text-secondary);
font-size: 0.95rem;
padding: 6px 12px;
border-radius: 6px;
transition: 0.2s;
}
.toc-link:hover,
.toc-link.active {
color: var(--text-primary);
background: var(--code-bg);
}
.toc-link.active {
border-left: 2px solid var(--accent);
border-radius: 0 6px 6px 0;
}
.content-area {
flex: 1;
max-width: 900px;
margin-left: 40px;
}
/* Header & Typography */
.readme-header {
border-bottom: 1px solid var(--border-color);
padding-bottom: 32px;
margin-bottom: 40px;
}
.readme-header h1 {
font-size: 2.5rem;
margin-bottom: 16px;
}
.version {
font-size: 1rem;
color: var(--text-secondary);
font-weight: 400;
background: var(--code-bg);
padding: 4px 10px;
border-radius: 40px;
margin-left: 12px;
}
.lead {
font-size: 1.25rem;
color: var(--text-secondary);
}
.badges {
display: flex;
gap: 12px;
margin-bottom: 24px;
}
.badge {
padding: 4px 12px;
border-radius: 40px;
font-size: 0.75rem;
font-weight: 600;
}
.badge-blue {
background: #053b6d;
color: #58a6ff;
}
.badge-green {
background: #1b4721;
color: #7ee787;
}
.badge-purple {
background: #4c2889;
color: #d2a8ff;
}
/* Sections */
.doc-section {
margin-bottom: 60px;
scroll-margin-top: 100px;
}
.doc-section h2 {
font-size: 1.75rem;
border-bottom: 1px solid var(--border-color);
padding-bottom: 12px;
margin-bottom: 24px;
}
.doc-section p {
margin-bottom: 20px;
}
.module-card {
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
transition: 0.2s;
}
.module-card:hover {
border-color: var(--accent);
}
.module-meta {
display: flex;
justify-content: space-between;
margin-top: 20px;
color: var(--text-secondary);
font-size: 0.85rem;
}
/* Skeleton */
.skeleton-line {
height: 16px;
background: var(--code-bg);
border-radius: 4px;
margin-bottom: 12px;
animation: pulse 1.5s infinite;
}
.skeleton-section {
height: 200px;
background: var(--code-bg);
border-radius: 12px;
margin-bottom: 40px;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
opacity: 0.5;
}
50% {
opacity: 0.8;
}
100% {
opacity: 0.5;
}
}
/* Modal & Playground */
.modal {
display: none;
position: fixed;
z-index: 2000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(8px);
align-items: center;
justify-content: center;
}
.modal.active {
display: flex;
}
.modal-content {
background: var(--bg-side);
width: 90%;
max-width: 1200px;
height: 85vh;
border-radius: 16px;
border: 1px solid var(--border-color);
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5);
}
.modal-header {
background: var(--bg-main);
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.file-info {
display: flex;
align-items: center;
gap: 12px;
font-weight: 600;
}
.file-info i {
color: var(--accent);
}
.header-actions {
display: flex;
gap: 16px;
align-items: center;
}
.btn-run {
background: var(--accent);
color: white;
border: none;
padding: 8px 20px;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.btn-run:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.btn-run:disabled {
background: var(--text-secondary);
cursor: not-allowed;
}
.btn-ghost {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-secondary);
padding: 8px 16px;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.btn-ghost:hover {
color: var(--text-primary);
background: var(--code-bg);
border-color: var(--text-secondary);
}
.btn-ghost.active {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-bg);
}
.close-modal {
background: transparent;
border: none;
color: var(--text-secondary);
font-size: 1.25rem;
cursor: pointer;
}
/* Monaco Editor Container */
#monaco-editor-container {
height: 100%;
width: 100%;
}
.playground-body {
flex: 1;
display: grid;
grid-template-columns: 1fr 400px;
overflow: hidden;
}
.editor-pane {
position: relative;
background: #1e1e1e;
overflow: hidden;
/* Monaco handles scroll */
}
/* Individual Lesson Hover */
.lesson-item {
background: transparent;
transition: all 0.2s ease;
}
.lesson-item:hover {
background: var(--accent-bg) !important;
padding-left: 16px !important;
/* Slight organic slide effect */
}
.editor-pane.edit-active {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.output-pane {
background: #010409;
border-left: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.output-header {
background: var(--bg-main);
padding: 8px 16px;
font-size: 0.75rem;
text-transform: uppercase;
font-weight: 700;
color: var(--text-secondary);
border-bottom: 1px solid var(--border-color);
}
#output-area {
flex: 1;
padding: 16px;
margin: 0;
font-family: var(--font-mono);
font-size: 0.9rem;
color: #a5d6ff;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
/* Scrollbar Style */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: var(--bg-side);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
/* Footer */
.docs-footer {
border-top: 1px solid var(--border-color);
padding: 40px 0;
margin-top: 80px;
text-align: center;
}
@media (max-width: 992px) {
.sidebar {
display: none;
}
.content-area {
margin-left: 0;
}
.playground-body {
grid-template-columns: 1fr;
grid-template-rows: 1fr 250px;
}
.output-pane {
border-left: none;
border-top: 1px solid var(--border-color);
}
}