refactor: implement pug and parcel

This commit is contained in:
2025-06-01 20:23:09 +02:00
parent e7d383b960
commit 310e284abe
8 changed files with 20271 additions and 1239 deletions

207
.pugrc Normal file
View File

@@ -0,0 +1,207 @@
{
"locals": {
"t": {
"meta": {
"title": "Web Platform Fundamentals: Building with Native HTML",
"description": "Master native HTML capabilities and progressive enhancement patterns. This guide demonstrates how leveraging built-in browser features reduces complexity, improves performance, and creates more maintainable codebases."
},
"hero": {
"title": "🚀 Web Platform Fundamentals",
"subtitle": "Master native HTML capabilities and progressive enhancement patterns. This guide demonstrates how leveraging built-in browser features reduces complexity, improves performance, and creates more maintainable codebases."
},
"sections": {
"disclosure": {
"title": "🎯 Progressive Disclosure Patterns",
"js_title": "JavaScript-Dependent Implementation",
"js_badge": "COMPLEX",
"js_warning": "Implementation Overhead: Manual ARIA state management, keyboard event handling, focus management, and screen reader compatibility.",
"js_content1": "Custom collapsible implementations require comprehensive event handling, state synchronization across components, and careful attention to accessibility requirements including proper ARIA attributes and keyboard navigation patterns.",
"js_content2": "Managing state across multiple collapsible sections introduces complexity in event coordination, memory management, and performance optimization, particularly when dealing with dynamic content loading.",
"js_cons_title": "❌ Architecture Complexity:",
"js_cons": [
"Event delegation and memory leak prevention",
"ARIA state synchronization and keyboard trap management",
"Cross-browser compatibility and polyfill requirements"
],
"native_title": "Native Details/Summary Elements",
"native_badge": "ZERO-JS",
"native_note": "Platform Integration: Built-in ARIA semantics, keyboard navigation, screen reader support, and browser-optimized animations.",
"native_semantic_title": "Semantic HTML Architecture",
"native_semantic_content": "The HTML5 details/summary pattern provides declarative progressive disclosure without JavaScript dependencies. Browser engines handle state management, accessibility, and user interaction patterns according to platform conventions.",
"native_enhancement_title": "Progressive Enhancement Benefits",
"native_enhancement_content": "Starting with functional HTML ensures graceful degradation across all environments. CSS and JavaScript become enhancement layers rather than functional requirements, improving reliability and reducing technical debt.",
"native_performance_title": "Performance Characteristics",
"native_performance_content": "Native elements eliminate bundle size overhead, reduce runtime memory consumption, and leverage browser optimizations unavailable to custom implementations. Hardware acceleration and efficient event handling come built-in.",
"native_pros_title": "✅ Platform Advantages:",
"native_pros": [
"Zero JavaScript footprint with full accessibility compliance",
"Browser-native performance optimizations and hardware acceleration",
"Consistent cross-platform behavior with no maintenance overhead"
]
},
"modals": {
"title": "🪟 Modal Dialog Patterns",
"js_title": "Custom Modal Implementation",
"js_badge": "BRITTLE",
"js_warning": "Critical Requirements: Focus trapping, inert background content, ESC key handling, backdrop click management, and ARIA modal semantics.",
"js_button": "Launch Custom Modal",
"js_modal_title": "Custom Modal Architecture",
"js_modal_content": "This approach requires managing the modal stack, preventing background interaction, coordinating focus management, and ensuring proper cleanup to avoid memory leaks and accessibility violations.",
"js_modal_issue": "Critical Issue: Background content remains accessible to screen readers and keyboard navigation without explicit inert management.",
"js_modal_close": "Close Modal",
"js_cons_title": "❌ Implementation Challenges:",
"js_cons": [
"Focus trap implementation and restoration complexity",
"Modal stack management and z-index coordination",
"Event cleanup and memory leak prevention"
],
"native_title": "Native Dialog Element",
"native_badge": "ROBUST",
"native_note": "Built-in Capabilities: Automatic focus trapping, inert background management, ESC key support, and proper modal semantics.",
"native_button": "Launch Native Dialog",
"native_dialog_title": "Native Dialog Element",
"native_dialog_content": "The HTML5 dialog element provides robust modal functionality with automatic focus management, backdrop interaction handling, and proper accessibility semantics built into the browser engine.",
"native_dialog_advantage": "Key Advantage: Background content becomes automatically inert, preventing interaction and screen reader access without additional implementation.",
"native_dialog_close": "Close Dialog",
"native_pros_title": "✅ Engine-Level Features:",
"native_pros": [
"Automatic focus trapping with proper restoration",
"Built-in ESC key handling and backdrop click support",
"Modal semantics and inert background management"
]
},
"validation": {
"title": "📝 Constraint Validation Patterns",
"js_title": "Custom Validation Logic",
"js_badge": "FRAGILE",
"js_warning": "Validation Complexity: Custom error messaging, timing coordination, accessibility announcements, and server-client synchronization.",
"js_email_label": "Email Validation",
"js_email_error": "Invalid email format detected",
"js_regex_label": "Pattern Matching (UUID)",
"js_regex_error": "UUID format required: 8-4-4-4-12 hex digits",
"js_submit": "Validate Form",
"js_cons_title": "❌ Validation Brittleness:",
"js_cons": [
"Regex patterns miss edge cases and internationalization",
"Error timing and accessibility announcement coordination",
"Client-server validation drift and synchronization issues"
],
"native_title": "Constraint Validation API",
"native_badge": "SPEC-COMPLIANT",
"native_note": "Standards-Based: Automatic error announcements, internationalized messages, and CSS pseudo-class integration.",
"native_email_label": "Email Validation",
"native_email_placeholder": "developer@example.org",
"native_uuid_label": "UUID Pattern",
"native_uuid_placeholder": "550e8400-e29b-41d4-a716-446655440000",
"native_uuid_title": "Enter a valid UUID format (e.g., 550e8400-e29b-41d4-a716-446655440000)",
"native_api_label": "API Endpoint URL",
"native_api_placeholder": "https://api.example.org/v1",
"native_submit": "Submit Form",
"native_pros_title": "✅ Specification Compliance:",
"native_pros": [
"RFC-compliant validation patterns with internationalization",
"Automatic error messaging in user's preferred language",
"CSS pseudo-classes (:valid, :invalid) for styling integration"
]
},
"progress": {
"title": "📊 Progress Indication Patterns",
"js_title": "Custom Progress Implementation",
"js_badge": "SEMANTIC-VOID",
"js_warning": "Accessibility Gap: No semantic meaning without explicit ARIA implementation and screen reader progress announcements.",
"js_text": "Build Progress:",
"js_button": "Start Build Process",
"js_cons_title": "❌ Semantic Limitations:",
"js_cons": [
"No inherent semantic meaning for assistive technologies",
"Manual ARIA live region management for progress updates",
"Custom animation performance and value interpolation"
],
"native_title": "Native Progress Element",
"native_badge": "SEMANTIC",
"native_note": "Semantic Integration: Built-in progress role, automatic ARIA value announcements, and indeterminate state support.",
"native_compilation": "Compilation Progress",
"native_compilation_text": "75% complete",
"native_indeterminate": "Indeterminate Operation (processing)",
"native_indeterminate_text": "Processing dependencies...",
"native_dynamic": "Dynamic Progress:",
"native_button": "Start Process",
"native_pros_title": "✅ Semantic Advantages:",
"native_pros": [
"Built-in progress role with automatic value announcements",
"Indeterminate state support for unknown duration operations",
"Platform-optimized rendering and animation performance"
]
},
"temporal": {
"title": "📅 Temporal Input Patterns",
"js_title": "Custom Date Picker",
"js_badge": "HEAVYWEIGHT",
"js_warning": "Implementation Scope: Calendar widget architecture, keyboard navigation, internationalization, timezone handling, and mobile touch optimization.",
"js_label": "Deployment Date (Custom)",
"js_placeholder": "YYYY-MM-DD",
"js_picker_content": "[Calendar widget implementation]\nRequires: Date manipulation library, internationalization,\nkeyboard navigation, mobile gesture handling,\ntimezone calculations, accessibility compliance.",
"js_cons_title": "❌ Implementation Overhead:",
"js_cons": [
"Large JavaScript libraries and complex calendar logic",
"Mobile UX inconsistency with platform date pickers",
"Internationalization complexity and timezone edge cases"
],
"native_title": "Native Temporal Inputs",
"native_badge": "PLATFORM-NATIVE",
"native_note": "OS Integration: Platform-consistent UI, automatic keyboard navigation, built-in validation, and internationalization support.",
"native_release_label": "Release Date",
"native_timestamp_label": "Build Timestamp",
"native_window_label": "Deployment Window",
"native_pros_title": "✅ Platform Integration:",
"native_pros": [
"Zero-bundle impact with native OS picker integration",
"Automatic validation and internationalization support",
"Consistent UX aligned with platform conventions"
]
},
"autocomplete": {
"title": "🔍 Autocomplete Patterns",
"js_title": "Custom Autocomplete",
"js_badge": "INTERACTION-HEAVY",
"js_warning": "Interaction Complexity: Dropdown positioning, keyboard navigation, ARIA combobox implementation, and mobile touch handling.",
"js_label": "Framework Search (Custom)",
"js_placeholder": "Type framework name...",
"js_cons_title": "❌ Interaction Management:",
"js_cons": [
"Complex ARIA combobox implementation and state management",
"Dropdown positioning and viewport collision detection",
"Mobile keyboard optimization and touch event handling"
],
"native_title": "Native Datalist Element",
"native_badge": "ZERO-CONFIG",
"native_note": "Native Combobox: Built-in ARIA combobox semantics, keyboard navigation, and platform-consistent interaction patterns.",
"native_framework_label": "JavaScript Framework",
"native_framework_placeholder": "Select or type framework...",
"native_language_label": "Programming Language",
"native_language_placeholder": "Choose language...",
"native_pros_title": "✅ Zero-Configuration Benefits:",
"native_pros": [
"Native combobox semantics with automatic ARIA support",
"Platform-consistent keyboard and interaction patterns",
"Form validation integration and graceful degradation"
]
}
},
"summary": {
"title": "Platform-First Development",
"content": "Native HTML elements represent years of browser engineering, accessibility research, and web standards evolution. They provide robust functionality with minimal implementation overhead and maximum compatibility.",
"approach_title": "The Progressive Enhancement Approach:",
"approach_items": [
"Build with semantic HTML that functions universally",
"Enhance visual design through CSS without breaking functionality",
"Layer JavaScript for complex interactions where native capabilities are insufficient"
],
"closing": "Leverage browser engines rather than reimplementing their functionality. Your codebase will be more maintainable, your users will have better experiences.",
"quote": "The most sophisticated architecture is often the one that uses existing, well-tested components. Native HTML elements provide decades of optimization and accessibility engineering—build upon this foundation rather than around it."
}
},
"lang": "en",
"dir": "ltr"
}
}

1226
index.html

File diff suppressed because it is too large Load Diff

228
index.pug Normal file
View File

@@ -0,0 +1,228 @@
include src/components/mixins
doctype html
html(lang=lang, dir=dir)
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title= t.meta.title
meta(name="description", content=t.meta.description)
link(rel="stylesheet", href="src/main.css")
body.search_plugin_added
//- Hero Section
.hero
h1= t.hero.title
p.subtitle= t.hero.subtitle
//- Progressive Disclosure Section
+section(t.sections.disclosure.title)
.comparison-grid
+badColumn(t.sections.disclosure.js_title, t.sections.disclosure.js_badge, t.sections.disclosure.js_warning)
+jsCollapsible("content1", "Custom Collapsible Architecture", t.sections.disclosure.js_content1)
+jsCollapsible("content2", "State Management Challenges", t.sections.disclosure.js_content2)
+consList(t.sections.disclosure.js_cons_title, t.sections.disclosure.js_cons)
+goodColumn(t.sections.disclosure.native_title, t.sections.disclosure.native_badge, t.sections.disclosure.native_note)
details(name="architecture")
summary= t.sections.disclosure.native_semantic_title
div= t.sections.disclosure.native_semantic_content
details(name="architecture")
summary= t.sections.disclosure.native_enhancement_title
div= t.sections.disclosure.native_enhancement_content
details(name="architecture")
summary= t.sections.disclosure.native_performance_title
div= t.sections.disclosure.native_performance_content
+prosList(t.sections.disclosure.native_pros_title, t.sections.disclosure.native_pros)
//- Modal Section
+section(t.sections.modals.title)
.comparison-grid
+badColumn(t.sections.modals.js_title, t.sections.modals.js_badge, t.sections.modals.js_warning)
button(onclick="openJSModal()")= t.sections.modals.js_button
#jsModal.js-modal-overlay
.js-modal-content
h4= t.sections.modals.js_modal_title
p= t.sections.modals.js_modal_content
p
strong= t.sections.modals.js_modal_issue
button(onclick="closeJSModal()")= t.sections.modals.js_modal_close
+consList(t.sections.modals.js_cons_title, t.sections.modals.js_cons)
+goodColumn(t.sections.modals.native_title, t.sections.modals.native_badge, t.sections.modals.native_note)
button(onclick="document.getElementById('nativeDialog').showModal()")= t.sections.modals.native_button
dialog#nativeDialog
h4= t.sections.modals.native_dialog_title
p= t.sections.modals.native_dialog_content
p
strong= t.sections.modals.native_dialog_advantage
button(onclick="document.getElementById('nativeDialog').close()")= t.sections.modals.native_dialog_close
+prosList(t.sections.modals.native_pros_title, t.sections.modals.native_pros)
//- Form Validation Section
+section(t.sections.validation.title)
.comparison-grid
+badColumn(t.sections.validation.js_title, t.sections.validation.js_badge, t.sections.validation.js_warning)
form(onsubmit="return validateJSForm(event)")
+formGroup(t.sections.validation.js_email_label)
input#js-email(name="email", type="text")
#email-error.js-error= t.sections.validation.js_email_error
+formGroup(t.sections.validation.js_regex_label)
input#js-regex(name="uuid", type="text")
#regex-error.js-error= t.sections.validation.js_regex_error
button(type="submit")= t.sections.validation.js_submit
+consList(t.sections.validation.js_cons_title, t.sections.validation.js_cons)
+goodColumn(t.sections.validation.native_title, t.sections.validation.native_badge, t.sections.validation.native_note)
form
+formGroup(t.sections.validation.native_email_label)
input#native-email(name="email", type="email", placeholder=t.sections.validation.native_email_placeholder, required)
+formGroup(t.sections.validation.native_uuid_label)
input#native-uuid(
name="uuid",
type="text",
pattern="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
placeholder=t.sections.validation.native_uuid_placeholder,
title=t.sections.validation.native_uuid_title,
required
)
+formGroup(t.sections.validation.native_api_label)
input#native-api(name="api", type="url", placeholder=t.sections.validation.native_api_placeholder)
button(type="submit")= t.sections.validation.native_submit
+prosList(t.sections.validation.native_pros_title, t.sections.validation.native_pros)
//- Progress Section
+section(t.sections.progress.title)
.comparison-grid
+badColumn(t.sections.progress.js_title, t.sections.progress.js_badge, t.sections.progress.js_warning)
.js-progress-container
#jsProgress.js-progress-bar
p= t.sections.progress.js_text + " "
span#jsProgressText 0%
button(onclick="startJSProgress()")= t.sections.progress.js_button
+consList(t.sections.progress.js_cons_title, t.sections.progress.js_cons)
+goodColumn(t.sections.progress.native_title, t.sections.progress.native_badge, t.sections.progress.native_note)
p= t.sections.progress.native_compilation
progress(max="100", value="75")= t.sections.progress.native_compilation_text
p= t.sections.progress.native_indeterminate
progress= t.sections.progress.native_indeterminate_text
p= t.sections.progress.native_dynamic + " "
span#nativeProgressText 0%
progress#nativeProgress(max="100", value="0") 0%
button(onclick="startNativeProgress()")= t.sections.progress.native_button
+prosList(t.sections.progress.native_pros_title, t.sections.progress.native_pros)
//- Temporal Input Section
+section(t.sections.temporal.title)
.comparison-grid
+badColumn(t.sections.temporal.js_title, t.sections.temporal.js_badge, t.sections.temporal.js_warning)
+formGroup(t.sections.temporal.js_label)
input#js-date(type="text", placeholder=t.sections.temporal.js_placeholder, readonly, onclick="openDatePicker()")
#datePicker(style="display: none; position: absolute; background: white; border: 1px solid #ccc; z-index: 1000")
p(style="padding: 20px; color: #666")= t.sections.temporal.js_picker_content
+consList(t.sections.temporal.js_cons_title, t.sections.temporal.js_cons)
+goodColumn(t.sections.temporal.native_title, t.sections.temporal.native_badge, t.sections.temporal.native_note)
+formGroup(t.sections.temporal.native_release_label)
input#native-date(name="date", type="date", value="2024-12-15", min="2024-01-01", max="2025-12-31")
+formGroup(t.sections.temporal.native_timestamp_label)
input#native-datetime(name="datetime", type="datetime-local", step="1")
+formGroup(t.sections.temporal.native_window_label)
input#native-time(name="time", type="time", min="02:00", max="18:00", step="900")
+prosList(t.sections.temporal.native_pros_title, t.sections.temporal.native_pros)
//- Autocomplete Section
+section(t.sections.autocomplete.title)
.comparison-grid
+badColumn(t.sections.autocomplete.js_title, t.sections.autocomplete.js_badge, t.sections.autocomplete.js_warning)
+formGroup(t.sections.autocomplete.js_label)
input#js-search(type="text", placeholder=t.sections.autocomplete.js_placeholder, autocomplete="off", onkeyup="filterFrameworks(this.value)")
#js-results(
style="display: none; position: absolute; background: var(--color-card); border: 1px solid var(--color-border); max-height: 200px; overflow-y: auto; z-index: 100"
)
+consList(t.sections.autocomplete.js_cons_title, t.sections.autocomplete.js_cons)
+goodColumn(t.sections.autocomplete.native_title, t.sections.autocomplete.native_badge, t.sections.autocomplete.native_note)
+formGroup(t.sections.autocomplete.native_framework_label)
input#native-framework(type="text", list="frameworks", placeholder=t.sections.autocomplete.native_framework_placeholder)
datalist#frameworks
option(value="React") React - Component-based library
option(value="Vue.js") Vue.js - Progressive framework
option(value="Angular") Angular - Full platform
option(value="Svelte") Svelte - Compile-time framework
option(value="Solid") Solid - Fine-grained reactivity
option(value="Alpine.js") Alpine.js - Minimal framework
option(value="Lit") Lit - Web Components library
option(value="Preact") Preact - Lightweight React alternative
+formGroup(t.sections.autocomplete.native_language_label)
input#native-language(type="text", list="languages", placeholder=t.sections.autocomplete.native_language_placeholder)
datalist#languages
option(value="JavaScript", label="JavaScript - Dynamic scripting")
option(value="TypeScript", label="TypeScript - Typed JavaScript")
option(value="Python", label="Python - General purpose")
option(value="Rust", label="Rust - Systems programming")
option(value="Go", label="Go - Concurrent systems")
option(value="WebAssembly", label="WebAssembly - High-performance web")
option(value="C", label="C - Low-level systems")
option(value="C++", label="C++ - High-performance systems")
option(value="C#", label="C# - .NET ecosystem")
option(value="Java", label="Java - Cross-platform enterprise")
option(value="Kotlin", label="Kotlin - Modern JVM language")
option(value="Swift", label="Swift - iOS and macOS")
option(value="Ruby", label="Ruby - Web development (Rails)")
option(value="PHP", label="PHP - Server-side scripting")
option(value="Perl", label="Perl - Text processing")
option(value="Haskell", label="Haskell - Functional programming")
option(value="Elixir", label="Elixir - Scalable applications")
option(value="Dart", label="Dart - Web and mobile apps")
option(value="Scala", label="Scala - Functional JVM language")
option(value="Lua", label="Lua - Lightweight scripting")
option(value="R", label="R - Statistical computing")
option(value="Julia", label="Julia - High-performance computing")
option(value="Shell", label="Shell - Command-line scripting")
option(value="MATLAB", label="MATLAB - Engineering and science")
+prosList(t.sections.autocomplete.native_pros_title, t.sections.autocomplete.native_pros)
//- Summary Section
.summary-box
h3= t.summary.title
p= t.summary.content
p
strong= t.summary.approach_title
ul(style="text-align: left; max-width: 600px; margin: 2rem auto")
each item in t.summary.approach_items
li= item
p= t.summary.closing
.zen-quote= t.summary.quote
script(src="src/main.js")

19042
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,38 @@
{
"name": "dhbw-html",
"version": "0.0.0",
"description": "",
"license": "ISC",
"author": "Michael W. Czechowski <mail@dailysh.it>",
"type": "commonjs",
"main": "index.html",
"scripts": {
"start": "npx serve . -p 1312 --cors",
"test": "echo \"Error: no test specified\" && exit 1"
}
"name": "html-over-js",
"version": "0.0.0",
"description": "",
"main": "index.pug",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "parcel --public-url / index.pug --open",
"build": "parcel build index.pug --no-minify",
"format": "prettier --write src/**/*.pug index.pug package.json && prettier --write --parser json .pugrc"
},
"author": "Michael Werner Czechowski <mail@dailysh.it>",
"license": "Apache-2.0",
"devDependencies": {
"@prettier/plugin-pug": "^3.1.0",
"parcel-bundler": "^1.10.3",
"prettier": "^3.3.3",
"pug": "^3.0.3"
},
"prettier": {
"singleQuote": false,
"tabWidth": 4,
"semi": true,
"arrowParens": "always",
"useTabs": true,
"jsxSingleQuote": false,
"bracketSpacing": false,
"printWidth": 160,
"trailingComma": "all",
"bracketSameLine": false,
"htmlWhitespaceSensitivity": "strict",
"proseWrap": "never",
"singleAttributePerLine": false,
"plugins": [
"@prettier/plugin-pug"
]
}
}

48
src/components/mixins.pug Normal file
View File

@@ -0,0 +1,48 @@
mixin section(title)
.section
h2.section-title= title
block
mixin badColumn(title, badge, warning)
.column.bad
h3= title + " "
span.perf-indicator.perf-slow= badge
.accessibility-note
strong ⚠️ #{warning}
block
mixin goodColumn(title, badge, note)
.column.good
h3= title + " "
span.perf-indicator.perf-fast= badge
.accessibility-note
strong 🎯 #{note}
block
mixin jsCollapsible(id, title, content)
.js-wrapper
.js-content(id=id)= content
button.js-collapsible(onclick=`toggleContent('${id}')`)= title
mixin formGroup(label)
.form-group
label= label
block
mixin consList(title, items)
.pros-cons
h4.cons= title
ul
each item in items
li= item
mixin prosList(title, items)
.pros-cons
h4.pros= title
ul
each item in items
li= item

641
src/main.css Normal file
View File

@@ -0,0 +1,641 @@
:root {
--color-bg: #ffffff;
--color-card: #f8f9fa;
--color-primary: #0066cc;
--color-primary-hover: #0052a3;
--color-danger: #dc3545;
--color-warning: #856404;
--color-warning-bg: #fff3cd;
--color-content-bg: #f1f3f4;
--color-border: #dee2e6;
--color-text: #212529;
--color-text-muted: #6c757d;
--color-code-bg: #f8f9fa;
--color-code-text: #0066cc;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
--radius: 8px;
--focus-outline: 2px solid #0066cc;
--focus-outline-danger: 2px solid #dc3545;
--focus-offset: 2px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
background: var(--color-bg);
color: var(--color-text);
min-height: 100vh;
}
.hero {
text-align: center;
margin-bottom: 4rem;
padding: 3rem;
background: var(--color-card);
border-radius: var(--radius);
box-shadow: var(--shadow);
border: 1px solid var(--color-border);
}
.hero:focus-within {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
.hero h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
margin-bottom: 1.5rem;
color: var(--color-primary);
font-weight: 700;
letter-spacing: -0.02em;
}
.hero .subtitle {
font-size: 1.125rem;
color: var(--color-text-muted);
max-width: 700px;
margin: 0 auto;
line-height: 1.5;
}
.comparison-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin: 2rem 0;
}
.section {
margin: 4rem 0;
}
.section-title {
font-size: 2rem;
margin-bottom: 2rem;
padding: 1.5rem 0;
text-align: center;
color: var(--color-primary);
position: relative;
font-weight: 600;
}
.section-title::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 2px;
background: var(--color-primary);
}
.column {
background: var(--color-card);
padding: 2rem;
border-radius: var(--radius);
box-shadow: var(--shadow);
border: 1px solid var(--color-border);
transition: box-shadow 0.2s ease;
position: relative;
}
.column:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.column:focus-within {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
.column.bad:focus-within {
outline: var(--focus-outline-danger);
}
.column h3 {
color: var(--color-text);
border-bottom: 1px solid var(--color-border);
padding-bottom: 1rem;
margin-bottom: 2rem;
font-size: 1.25rem;
font-weight: 600;
}
.bad {
border-left: 3px solid var(--color-danger);
}
.bad h3::before {
content: '⚠️ ';
}
.bad button {
background: var(--color-danger);
}
.bad button:hover {
background: var(--color-danger);
}
.bad button:focus {
outline: var(--focus-outline-danger);
outline-offset: var(--focus-offset);
}
.good {
border-left: 3px solid var(--color-primary);
}
.good h3::before {
content: '✅ ';
}
.accessibility-note {
background: var(--color-warning-bg);
border: 1px solid #ffc107;
color: var(--color-warning);
padding: 1rem;
border-radius: var(--radius);
margin: 1.5rem 0;
font-size: 0.875rem;
}
.code-block {
/*display: none;*/
background: var(--color-code-bg);
color: var(--color-code-text);
padding: 2rem 1.5rem 1rem;
border-radius: var(--radius);
overflow-x: auto;
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
font-size: 0.875rem;
margin: 1.5rem 0;
border: 1px solid var(--color-border);
position: relative;
}
.code-block:focus {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
.code-block::before {
content: 'CODE';
position: absolute;
top: -6px;
right: 15px;
background: var(--color-primary);
color: white;
padding: 10px 8px 0;
font-size: 0.7rem;
border-radius: 4px;
font-weight: 600;
}
.pros-cons {
margin: 1.5rem 0;
}
.pros-cons h4 {
margin-bottom: 1rem;
color: var(--color-text);
font-size: 1rem;
font-weight: 600;
}
.pros-cons ul {
list-style: none;
padding-left: 0;
}
.pros-cons li {
padding: 0.5rem 0;
padding-left: 1.5rem;
position: relative;
}
.pros li::before {
content: '✓';
position: absolute;
left: 0;
color: var(--color-primary);
font-weight: bold;
}
.cons li::before {
content: '✗';
position: absolute;
left: 0;
color: var(--color-danger);
font-weight: bold;
}
/* Interactive Elements */
.js-collapsible {
color: white;
cursor: pointer;
padding: 12px 16px;
border: none;
text-align: left;
outline: none;
font-size: 1rem;
border-radius: var(--radius);
margin-bottom: 1rem;
width: 100%;
font-family: inherit;
font-weight: 500;
}
.js-wrapper {
display: flex;
flex-direction: column-reverse;
}
.js-content {
display: none;
background: var(--color-content-bg);
border: 1px solid var(--color-border);
border-radius: 0 0 var(--radius) var(--radius);
margin-bottom: 1rem;
}
.js-content.active {
display: block;
padding: 0.5rem;
}
.js-content.active + .js-collapsible {
border-radius: var(--radius) var(--radius) 0 0;
margin-bottom: 0;
}
details {
background: var(--color-card);
border: 1px solid var(--color-border);
border-radius: var(--radius);
margin-bottom: 1rem;
overflow: hidden;
}
details:focus-within {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
summary {
background: var(--color-primary);
color: white;
padding: 12px 16px;
cursor: pointer;
font-size: 1rem;
list-style: none;
font-weight: 500;
transition: all 0.2s ease;
}
summary:hover {
background: var(--color-primary-hover);
}
summary:focus {
outline: var(--focus-outline);
outline-offset: -2px;
}
summary::-webkit-details-marker {
display: none;
}
summary::before {
content: '▶';
margin-right: 8px;
transition: transform 0.2s ease;
display: inline-block;
}
details[open] summary::before {
transform: rotate(90deg);
}
details > div {
padding: 16px;
background: var(--color-content-bg);
}
/* Modal Styles */
.js-modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
z-index: 1000;
}
.js-modal-overlay.active {
display: flex;
align-items: center;
justify-content: center;
}
.js-modal-content {
margin: auto;
background: var(--color-bg);
padding: 2rem;
border-radius: var(--radius);
max-width: 500px;
width: 90%;
border: 1px solid var(--color-border);
box-shadow: var(--shadow);
}
.js-modal-content:focus {
outline: var(--focus-outline-danger);
outline-offset: var(--focus-offset);
}
dialog {
margin: auto;
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 2rem;
background: var(--color-bg);
color: var(--color-text);
box-shadow: var(--shadow);
max-width: 500px;
width: 90vw;
}
dialog:focus {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.6);
}
/* Form Styles */
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--color-text);
}
.form-group input, .form-group textarea, .form-group select {
width: 100%;
padding: 10px 12px;
border: 2px solid var(--color-border);
border-radius: var(--radius);
font-size: 1rem;
background: var(--color-bg);
color: var(--color-text);
transition: all 0.2s ease;
font-family: inherit;
}
.form-group input:focus, .form-group textarea:focus, .form-group select:focus {
outline: var(--focus-outline);
outline-offset: 2px;
border-color: var(--color-border);
}
.form-group input:invalid {
border-color: var(--color-danger);
}
.form-group input:valid {
border-color: var(--color-primary);
}
.js-error {
color: var(--color-danger);
font-size: 0.875rem;
margin-top: 0.5rem;
display: none;
}
.js-error.active {
display: block;
}
/* Progress Styles */
.js-progress-container {
width: 100%;
background: var(--color-border);
border-radius: 4px;
height: 20px;
margin: 1.5rem 0;
overflow: hidden;
}
.js-progress-bar {
height: 100%;
background: var(--color-danger);
width: 0;
transition: width 0.3s ease;
border-radius: 4px;
}
progress {
width: 100%;
height: 20px;
margin: 1.5rem 0;
border-radius: 4px;
border: none;
background: var(--color-border);
}
progress:focus {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
progress::-webkit-progress-bar {
background-color: var(--color-border);
border-radius: 4px;
}
progress::-webkit-progress-value {
background: var(--color-primary);
border-radius: 4px;
}
progress::-moz-progress-bar {
background: var(--color-primary);
border-radius: 4px;
}
/* Button Styles */
button {
background: var(--color-primary);
color: white;
border: none;
padding: 10px 16px;
border-radius: var(--radius);
cursor: pointer;
font-size: 1rem;
margin: 0.5rem 0.5rem 0.5rem 0;
font-weight: 500;
transition: all 0.2s ease;
font-family: inherit;
}
button:hover {
background: var(--color-primary-hover);
transform: translateY(-1px);
}
button:focus {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
button:disabled {
background: var(--color-text-muted);
cursor: not-allowed;
transform: none;
}
/* Component Styles */
.datalist-container {
position: relative;
}
input[list] {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23666' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
background-position: right 12px center;
background-repeat: no-repeat;
background-size: 16px;
padding-right: 36px;
}
.slider-container {
margin: 2rem 0;
}
input[type="range"] {
width: 100%;
height: 6px;
border-radius: 3px;
background: var(--color-border);
outline: none;
-webkit-appearance: none;
}
input[type="range"]:focus {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
input[type="range"]::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--color-primary);
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--color-primary);
cursor: pointer;
border: none;
}
.color-input {
width: 50px;
height: 32px;
border: 1px solid var(--color-border);
border-radius: var(--radius);
cursor: pointer;
}
.color-input:focus {
outline: var(--focus-outline);
outline-offset: var(--focus-offset);
}
.summary-box {
margin-top: 4rem;
padding: 2rem;
background: var(--color-card);
border-radius: var(--radius);
box-shadow: var(--shadow);
border: 1px solid var(--color-border);
text-align: center;
}
.summary-box h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: var(--color-primary);
font-weight: 600;
}
.zen-quote {
font-style: italic;
color: var(--color-text-muted);
font-size: 1rem;
margin-top: 1.5rem;
border-left: 3px solid var(--color-primary);
padding-left: 1rem;
}
@media (max-width: 768px) {
.comparison-grid {
grid-template-columns: 1fr;
}
body {
padding: 1rem;
}
.hero {
padding: 2rem;
}
.section-title {
font-size: 1.75rem;
}
}
/* Performance indicators */
.perf-indicator {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
margin-left: 8px;
}
.perf-fast {
background: rgba(0, 102, 204, 0.1);
color: var(--color-primary);
}
.perf-slow {
background: rgba(220, 53, 69, 0.1);
color: var(--color-danger);
}

71
src/main.js Normal file
View File

@@ -0,0 +1,71 @@
// JavaScript functions from original HTML
const frameworks = ['React', 'Vue.js', 'Angular', 'Svelte', 'Solid', 'Alpine.js', 'Lit', 'Preact', 'Qwik', 'SvelteKit', 'Next.js', 'Nuxt.js'];
function filterFrameworks(value) {
const results = document.getElementById('js-results');
if (!value) {
results.style.display = 'none';
return;
}
const filtered = frameworks.filter(fw => fw.toLowerCase().includes(value.toLowerCase()));
results.innerHTML = filtered.map(fw => `<div style="padding:8px; cursor:pointer;" onclick="selectFramework('${fw}')">${fw}</div>`).join('');
results.style.display = filtered.length ? 'block' : 'none';
}
function selectFramework(fw) {
document.getElementById('js-search').value = fw;
document.getElementById('js-results').style.display = 'none';
}
function startNativeProgress() {
const progress = document.getElementById('nativeProgress');
const text = document.getElementById('nativeProgressText');
let value = 0;
const interval = setInterval(() => {
value += Math.random() * 15;
if (value >= 100) {
value = 100;
clearInterval(interval);
}
progress.value = value;
text.textContent = Math.round(value) + '%';
}, 200);
}
function toggleContent(id) {
document.getElementById(id).classList.toggle("active");
}
function openJSModal() {
document.getElementById('jsModal').style.display = 'flex';
}
function closeJSModal() {
document.getElementById('jsModal').style.display = 'none';
}
function validateJSForm(event) {
event.preventDefault();
alert('Custom validation would run here');
return false;
}
function startJSProgress() {
const bar = document.getElementById('jsProgress');
const text = document.getElementById('jsProgressText');
let width = 0;
const interval = setInterval(() => {
width += Math.random() * 15;
if (width >= 100) {
width = 100;
clearInterval(interval);
}
bar.style.width = width + '%';
text.textContent = Math.round(width) + '%';
}, 200);
}
function openDatePicker() {
const picker = document.getElementById('datePicker');
picker.style.display = picker.style.display === 'none' ? 'block' : 'none';
}