Merge pull request #437 from dwmkerr/chore/cleanup

chore: cleanup
This commit is contained in:
Dave Kerr
2025-03-11 11:01:15 +00:00
committed by GitHub
20 changed files with 1309 additions and 17 deletions

View File

@@ -1 +0,0 @@
Testing

0
.github/website/backup/ideas.md vendored Normal file
View File

189
.github/website/backup/index2.html vendored Normal file
View File

@@ -0,0 +1,189 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hacker Laws</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet" />
<style>
/* Soft pastel parchment background */
body {
background-color: #fdf6e3;
color: #333;
padding-top: 70px; /* to account for sticky navbar */
}
/* Navbar customization */
.navbar-custom {
background-color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Header styling */
header h1 {
font-size: 2.5rem;
font-weight: bold;
}
header p.lead {
font-size: 1.25rem;
color: #555;
}
/* Law section container */
.law-section {
margin-bottom: 2rem;
padding: 1.5rem;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
/* Social sharing icons */
.social-sharing a {
margin-right: 0.75rem;
font-size: 1.2rem;
color: #555;
text-decoration: none;
}
.social-sharing a:hover {
color: #000;
}
/* Back to top link styling */
.back-to-top a {
font-size: 0.9rem;
text-decoration: none;
color: #007bff;
}
.back-to-top a:hover {
text-decoration: underline;
}
</style>
</head>
<body id="top">
<!-- Sticky Navbar -->
<nav class="navbar navbar-expand-lg navbar-custom fixed-top">
<div class="container">
<a class="navbar-brand fw-bold" href="#top">Hacker Laws</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu" aria-controls="navMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navMenu">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-book"></i> Effective Shell</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-cup"></i> Sponsor</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-brain"></i> Terminal AI</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-github"></i> GitHub</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Header -->
<header class="container my-4">
<h1>Hacker Laws</h1>
<p class="lead">Laws, Theories, Principles and Patterns that developers will find useful.</p>
</header>
<!-- Main Content -->
<main class="container">
<!-- Introduction Section -->
<section id="introduction" class="law-section">
<h2>Introduction</h2>
<p>There are lots of laws which people discuss when talking about development. This repository is a reference and overview of some of the most common ones. Please share and submit PRs!</p>
<p><strong>Note:</strong> This repo contains an explanation of some laws, principles and patterns, but does not <em>advocate</em> for any of them. Whether they should be applied will always be a matter of debate, and greatly dependent on what you are working on.</p>
<!-- Social Sharing Icons -->
<div class="social-sharing">
<a href="#" title="Share on Twitter"><i class="bi bi-twitter"></i></a>
<a href="#" title="Share on Facebook"><i class="bi bi-facebook"></i></a>
<a href="#" title="Share on LinkedIn"><i class="bi bi-linkedin"></i></a>
</div>
<!-- Back to Top Options (choose one) -->
<div class="back-to-top mt-2">
<a href="#top">↑ Top</a>
<!-- Alternative options:
<a href="#top">Back to Top</a>
<a href="#top">Return to Top</a>
<a href="#top">Go Up</a>
<a href="#top">Scroll Up</a>
-->
</div>
</section>
<!-- 9091 Principle (1% Rule) Section -->
<section id="9091-principle" class="law-section">
<h2>9091 Principle (1% Rule)</h2>
<p>The 90-9-1 principle suggests that within an internet community such as a wiki, 90% of participants only consume content, 9% edit or modify content and 1% of participants add content.</p>
<p>Real-world examples:</p>
<ul>
<li>A 2014 study of four digital health social networks found the top 1% created 73% of posts, the next 9% accounted for an average of ~25% and the remaining 90% accounted for an average of 2%.</li>
</ul>
<p>See Also: <a href="#the-pareto-principle-the-8020-rule">Pareto Principle</a></p>
<!-- Social Sharing Icons -->
<div class="social-sharing">
<a href="#" title="Share on Twitter"><i class="bi bi-twitter"></i></a>
<a href="#" title="Share on Facebook"><i class="bi bi-facebook"></i></a>
<a href="#" title="Share on LinkedIn"><i class="bi bi-linkedin"></i></a>
</div>
<!-- Back to Top Options -->
<div class="back-to-top mt-2">
<a href="#top">↑ Top</a>
</div>
</section>
<!-- 9090 Rule Section -->
<section id="9090-rule" class="law-section">
<h2>9090 Rule</h2>
<p>The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.</p>
<p>This is a wry reinterpretation of the <a href="#the-pareto-principle-the-8020-rule">Pareto Principle</a> (or 80-20 rule) that highlights the real-world challenges of completing engineering work. This sentiment is also echoed in <a href="#hofstadters-law">Hofstadter's Law</a>.</p>
<!-- Social Sharing Icons -->
<div class="social-sharing">
<a href="#" title="Share on Twitter"><i class="bi bi-twitter"></i></a>
<a href="#" title="Share on Facebook"><i class="bi bi-facebook"></i></a>
<a href="#" title="Share on LinkedIn"><i class="bi bi-linkedin"></i></a>
</div>
<!-- Back to Top Options -->
<div class="back-to-top mt-2">
<a href="#top">↑ Top</a>
</div>
</section>
<!-- Additional law sections would follow the same structure -->
</main>
<!-- Footer -->
<footer class="container text-center my-4">
<p>&copy; 2025 Hacker Laws</p>
</footer>
<!-- Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Optional: Smooth scrolling for in-page links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetElem = document.querySelector(this.getAttribute('href'));
if (targetElem) {
targetElem.scrollIntoView({ behavior: 'smooth' });
}
});
});
</script>
</body>
</html>

194
.github/website/backup/index3.html vendored Normal file
View File

@@ -0,0 +1,194 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hacker Laws</title>
<!-- Google Font for elegant serif fonts -->
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&display=swap" rel="stylesheet">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet" />
<style>
/* Use an elegant serif font and a clean, minimal palette */
body {
font-family: 'Libre Baskerville', Georgia, serif;
background-color: #fff;
color: #333;
padding-top: 70px; /* account for sticky navbar */
}
.container {
max-width: 800px;
}
/* Simplified Navbar */
.navbar-custom {
background-color: #fff;
border-bottom: 1px solid #e5e5e5;
}
.navbar-brand,
.nav-link {
font-weight: 700;
}
/* Centered, minimal header */
header {
text-align: center;
margin-bottom: 2rem;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
header p.lead {
font-size: 1.25rem;
color: #555;
}
/* Law section styling: simple borders instead of shadows */
.law-section {
margin-bottom: 2rem;
padding: 1.5rem;
background-color: #fff;
border-bottom: 1px solid #e5e5e5;
}
/* Social sharing icons remain the same */
.social-sharing a {
margin-right: 0.75rem;
font-size: 1.2rem;
color: #555;
text-decoration: none;
}
.social-sharing a:hover {
color: #000;
}
/* Back to top link styling */
.back-to-top a {
font-size: 0.9rem;
text-decoration: none;
color: #007bff;
}
.back-to-top a:hover {
text-decoration: underline;
}
</style>
</head>
<body id="top">
<!-- Sticky Navbar -->
<nav class="navbar navbar-expand-lg navbar-custom fixed-top">
<div class="container">
<a class="navbar-brand" href="#top">Hacker Laws</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu" aria-controls="navMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navMenu">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-book"></i> Effective Shell</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-cup"></i> Sponsor</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-brain"></i> Terminal AI</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-github"></i> GitHub</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Header -->
<header class="container my-4">
<h1>Hacker Laws</h1>
<p class="lead">Laws, Theories, Principles and Patterns that developers will find useful.</p>
</header>
<!-- Main Content -->
<main class="container">
<!-- Introduction Section -->
<section id="introduction" class="law-section">
<h2>Introduction</h2>
<p>There are lots of laws which people discuss when talking about development. This repository is a reference and overview of some of the most common ones. Please share and submit PRs!</p>
<p><strong>Note:</strong> This repo contains an explanation of some laws, principles and patterns, but does not <em>advocate</em> for any of them. Whether they should be applied will always be a matter of debate, and greatly dependent on what you are working on.</p>
<!-- Social Sharing Icons -->
<div class="social-sharing">
<a href="#" title="Share on Twitter"><i class="bi bi-twitter"></i></a>
<a href="#" title="Share on Facebook"><i class="bi bi-facebook"></i></a>
<a href="#" title="Share on LinkedIn"><i class="bi bi-linkedin"></i></a>
</div>
<!-- Back to Top -->
<div class="back-to-top mt-2">
<a href="#top">↑ Top</a>
</div>
</section>
<!-- 9091 Principle (1% Rule) Section -->
<section id="9091-principle" class="law-section">
<h2>9091 Principle (1% Rule)</h2>
<p>The 90-9-1 principle suggests that within an internet community such as a wiki, 90% of participants only consume content, 9% edit or modify content and 1% of participants add content.</p>
<p>Real-world examples:</p>
<ul>
<li>A 2014 study of four digital health social networks found the top 1% created 73% of posts, the next 9% accounted for an average of ~25% and the remaining 90% accounted for an average of 2%.</li>
</ul>
<p>See Also: <a href="#the-pareto-principle-the-8020-rule">Pareto Principle</a></p>
<!-- Social Sharing Icons -->
<div class="social-sharing">
<a href="#" title="Share on Twitter"><i class="bi bi-twitter"></i></a>
<a href="#" title="Share on Facebook"><i class="bi bi-facebook"></i></a>
<a href="#" title="Share on LinkedIn"><i class="bi bi-linkedin"></i></a>
</div>
<!-- Back to Top -->
<div class="back-to-top mt-2">
<a href="#top">↑ Top</a>
</div>
</section>
<!-- 9090 Rule Section -->
<section id="9090-rule" class="law-section">
<h2>9090 Rule</h2>
<p>The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.</p>
<p>This is a wry reinterpretation of the <a href="#the-pareto-principle-the-8020-rule">Pareto Principle</a> (or 80-20 rule) that highlights the real-world challenges of completing engineering work. This sentiment is also echoed in <a href="#hofstadters-law">Hofstadter's Law</a>.</p>
<!-- Social Sharing Icons -->
<div class="social-sharing">
<a href="#" title="Share on Twitter"><i class="bi bi-twitter"></i></a>
<a href="#" title="Share on Facebook"><i class="bi bi-facebook"></i></a>
<a href="#" title="Share on LinkedIn"><i class="bi bi-linkedin"></i></a>
</div>
<!-- Back to Top -->
<div class="back-to-top mt-2">
<a href="#top">↑ Top</a>
</div>
</section>
</main>
<!-- Footer -->
<footer class="container text-center my-4">
<p>&copy; 2025 Hacker Laws</p>
</footer>
<!-- Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Smooth scrolling for in-page links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetElem = document.querySelector(this.getAttribute('href'));
if (targetElem) {
targetElem.scrollIntoView({ behavior: 'smooth' });
}
});
});
</script>
</body>
</html>

0
.github/website/backup/index4.html vendored Normal file
View File

1
.github/website/build/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*

161
.github/website/generate.py vendored Normal file
View File

@@ -0,0 +1,161 @@
"""Generate the Hacker Laws website from the Hacker Laws README"""
import argparse
import os
import shutil
from jinja2 import Environment, FileSystemLoader
import markdown
from bs4 import BeautifulSoup
def bisect_text(content: str, bisect_line: str) -> tuple[str, str]:
lines = content.splitlines()
head = []
tail = []
found = False
for line in lines:
if found is False and line == bisect_line:
found = True
continue
if found:
tail.append(line)
else:
head.append(line)
return ("\n".join(head), "\n".join(tail))
def load_template():
"""Load Jinja2 template from the specified directory."""
env = Environment(loader=FileSystemLoader(TEMPLATE_DIR))
return env.get_template(TEMPLATE_FILE)
def prepare_markdown(path: str) -> str:
"""
Pre-process the README markdown by removing content we will not show in
the final website.
"""
# Load the markdown content.
with open(path, "r", encoding="utf-8") as f:
content = f.read()
return content
def parse_markdown(markdown_content: str):
(_, remains) = bisect_text(markdown_content, "---")
(links, remains) = bisect_text(remains, "---")
(_, content) = bisect_text(remains, "<!-- vim-markdown-toc -->")
md = markdown.Markdown(extensions=['toc'])
links = md.convert(links)
print(f"links: {links}")
md.convert(content)
toc = md.toc
markdown_sections = content.split("\n#") # Split by Markdown headings
sections = []
laws = []
for markdown_section in markdown_sections:
if markdown_section.strip():
lines = markdown_section.split("\n", 1)
title = lines[0].strip("# ").strip()
content = md.convert(lines[1] if len(lines) > 1 else "")
full_content = md.convert(markdown_section)
id = title.lower().replace(" ", "-")
laws.append({"title": title, "content": content, "id": id})
sections.append({
"title": title,
"content": content,
"id": id,
"full_content": full_content
})
return (links, toc, sections)
def extract_static_files(html_content, output_dir):
"""
Extract linked CSS, JS, and image files and copy them to the output
directory.
"""
soup = BeautifulSoup(html_content, "html.parser")
files_to_copy = []
# Extract <link> stylesheets
for link in soup.find_all("link", href=True):
href = link["href"]
if not href.startswith(("http", "//")): # Ignore external links
files_to_copy.append(href)
# Extract <script> files
for script in soup.find_all("script", src=True):
src = script["src"]
if not src.startswith(("http", "//")):
files_to_copy.append(src)
# Extract <img> files
for img in soup.find_all("img", src=True):
src = img["src"]
if not src.startswith(("http", "//")):
files_to_copy.append(src)
# Copy files to the output directory
for file_path in files_to_copy:
src_path = os.path.join(TEMPLATE_DIR, file_path)
dest_path = os.path.join(output_dir, file_path)
if os.path.exists(src_path): # Ensure file exists before copying
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
shutil.copy2(src_path, dest_path)
print(f"📂 Copied: {src_path}{dest_path}")
else:
print(f"⚠️ Warning: Missing file {src_path} (skipping)")
return files_to_copy
def generate_site(markdown_content: str, output_dir: str):
"""Generate the static HTML file from Markdown and Jinja2 template."""
template = load_template()
(links, toc, sections) = parse_markdown(markdown_content)
# Ensure output directory exists
os.makedirs(output_dir, exist_ok=True)
# Render HTML
html_output = template.render(links=links, toc=toc, sections=sections)
# Save HTML to output directory
output_file = os.path.join(output_dir, "index.html")
with open(output_file, "w", encoding="utf-8") as f:
f.write(html_output)
print(f"✅ Static site generated: {output_file}")
# Copy static files (CSS, JS, images)
extract_static_files(html_output, output_dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate a static site from Markdown.")
parser.add_argument("-o", "--output-dir", default="build", help="Directory to save the generated site.")
args = parser.parse_args()
# Read environment variables with defaults
TEMPLATE_FILE = os.getenv("TEMPLATE_FILE", "template.html")
TEMPLATE_DIR = os.getenv("TEMPLATE_DIR", ".")
template_path = f"{TEMPLATE_DIR}/{TEMPLATE_FILE}"
markdown_path = os.getenv("MARKDOWN_FILE", "laws.md")
output_dir = args.output_dir
print(f"📝 Loading template from: {template_path}")
print(f"📖 Loading markdown from: {markdown_path}")
print(f"💾 Outputting files to: {output_dir}")
# First, extract that markdown that we want to process.
markdown_content = prepare_markdown(markdown_path)
# Generate the site from the markdown.
generate_site(markdown_content, args.output_dir)

38
.github/website/makefile vendored Normal file
View File

@@ -0,0 +1,38 @@
SHELL := /bin/bash
TEMPLATE_DIR=src
TEMPLATE_FILE=index.html.jinja
MARKDOWN_FILE=../../README.md
OUTPUT_FILE=build/index.html
default: help
.PHONY: help
help: # Show help for each of the Makefile recipes.
@grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | sort | while read -r l; do printf "\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; done
.PHONY: install
install: # 📦 install dependencies
@echo "📦 Installing dependencies..."
pip install -r requirements.txt
.PHONY: build
build: #🔨 building static site
@echo "🔨 Building static site..."
cp -rf ../../images ./build
TEMPLATE_FILE=$(TEMPLATE_FILE) MARKDOWN_FILE=$(MARKDOWN_FILE) OUTPUT_FILE=$(OUTPUT_FILE) TEMPLATE_DIR=$(TEMPLATE_DIR) \
python generate.py
.PHONY: serve
serve: # 🚀 start local server
@echo "🚀 Starting local server at http://localhost:8000..."
python3 -m http.server 8000
.PHONY: watch
watch: build # 👀 Watch for changes...
@echo "👀 Watching for changes..."
watchmedo shell-command --patterns="$(MARKDOWN_FILE);*.py;src/*" --command="make build" .
.PHONY: clean
clean: #🧹 Clean up generated files
@echo "🧹 Cleaning up generated files..."
rm -f $(OUTPUT)

4
.github/website/requirements.txt vendored Normal file
View File

@@ -0,0 +1,4 @@
markdown
jinja2
watchdog
beautifulsoup4

3
.github/website/src/favicon.svg vendored Normal file
View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16 0 0 7.16 0 16C0 23.08 4.58 29.06 10.94 31.18C11.74 31.32 12.04 30.84 12.04 30.42C12.04 30.04 12.02 28.78 12.02 27.44C8 28.18 6.96 26.46 6.64 25.56C6.46 25.1 5.68 23.68 5 23.3C4.44 23 3.64 22.26 4.98 22.24C6.24 22.22 7.14 23.4 7.44 23.88C8.88 26.3 11.18 25.62 12.1 25.2C12.24 24.16 12.66 23.46 13.12 23.06C9.56 22.66 5.84 21.28 5.84 15.16C5.84 13.42 6.46 11.98 7.48 10.86C7.32 10.46 6.76 8.82 7.64 6.62C7.64 6.62 8.98 6.2 12.04 8.26C13.32 7.9 14.68 7.72 16.04 7.72C17.4 7.72 18.76 7.9 20.04 8.26C23.1 6.18 24.44 6.62 24.44 6.62C25.32 8.82 24.76 10.46 24.6 10.86C25.62 11.98 26.24 13.4 26.24 15.16C26.24 21.3 22.5 22.66 18.94 23.06C19.52 23.56 20.02 24.52 20.02 26.02C20.02 28.16 20 29.88 20 30.42C20 30.84 20.3 31.34 21.1 31.18C27.42 29.06 32 23.06 32 16C32 7.16 24.84 0 16 0V0Z" fill="#24292E"/>
</svg>

After

Width:  |  Height:  |  Size: 959 B

78
.github/website/src/index.html.jinja vendored Normal file
View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hacker Laws</title>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-RGJ5TDHWY9"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-RGJ5TDHWY9');
</script>
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&family=Inter:wght@400;600&display=swap" rel="stylesheet">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet" />
<link rel="stylesheet" href="styles.css">
</head>
<body id="top">
<nav class="navbar navbar-expand-lg fixed-top bg-dark" data-bs-theme="dark">
<div class="container">
<a class="navbar-brand" href="#top">Hacker Laws</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navMenu">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="https://effective-shell.com" target="_blank"><i class="bi bi-book"></i> Effective Shell</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/dwmkerr/terminal-ai" target="_blank"><i class="bi bi-terminal"></i> Terminal AI</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/sponsors/dwmkerr" target="_blank"><i class="bi bi-cup-hot"></i> Sponsor</a></li>
</ul>
<a href="https://github.com/dwmkerr/hacker-laws" target="_blank"><button class="btn btn-outline-light" type="submit"><i class="bi bi-github"></i> GitHub</button></a>
</div>
</div>
</nav>
<header class="container">
<h1>Hacker Laws</h1>
<p class="lead">Laws, Theories, Principles and Patterns that developers will find useful.</p>
</header>
<main class="container">
<!-- Quick links. -->
{{ links }}
<hr>
<!-- The table of contents. -->
{{ toc }}
<hr>
<!-- Each of the sections - most of which are laws. -->
{% for section in sections %}
<section id="{{ section.id }}" class="law-section">
{{ section.full_content | safe }}
<div class="social-sharing">
<a href="https://twitter.com/intent/tweet?url=https://hacker-laws.com/#{{ section.id}}?hashtags=example" title="Share on Twitter" target="_blank"><i class="bi bi-twitter"></i></a>
<a href="https://facebook.com/share?url=https://hacker-laws.com/&num;{{ section.id }}" title="Share on Facebook" target="_blank"><i class="bi bi-facebook"></i></a>
<a href="#" onclick="navigator.clipboard.writeText(window.location.href + '#{{ section.id }}'); alert('Copied!');" title="Copy Link" target="_blank"><i class="bi bi-clipboard"></i></a>
</div>
<div class="back-to-top"><a href="#top">↑ Back to Top</a></div>
</section>
{% endfor %}
</main>
<footer class="container text-center my-4">
<p>&copy; 2025 Hacker Laws</p>
</footer>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="script.js"></script>
</body>
</html>

18
.github/website/src/script.js vendored Normal file
View File

@@ -0,0 +1,18 @@
$(document).ready(function() {
$("h1, h2, h3, h4, h5, h6").each(function() {
var $heading = $(this);
var headingId = $heading.attr("id") || $heading.text().trim().toLowerCase().replace(/\s+/g, "-");
// Ensure a unique ID
$heading.attr("id", headingId);
// Create the anchor link
var $anchor = $('<a>')
.attr("href", "#" + headingId)
.addClass("header-link")
.html("#");
// Append to the heading
$heading.append($anchor);
});
});

73
.github/website/src/styles.css vendored Normal file
View File

@@ -0,0 +1,73 @@
html {
scroll-behavior: auto !important;
}
body {
font-family: 'Inter', sans-serif;
background-color: #fff;
color: #333;
padding-top: 70px;
}
.container {
max-width: 800px;
}
header {
text-align: center;
margin-bottom: 2rem;
padding: 2rem 0;
border-bottom: 1px solid #e5e5e5;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Libre Baskerville', serif;
/* Avoid scrolling under the sticky header. */
scroll-margin-top: 80px;
}
.law-section {
margin-bottom: 2rem;
padding: 1.5rem;
background-color: #fff;
border-bottom: 1px solid #e5e5e5;
position: relative;
}
.law-section h2 {
position: relative;
display: flex;
align-items: center;
}
.law-section h2 a.anchor {
text-decoration: none;
color: #999;
margin-left: 0.5rem;
visibility: hidden;
}
.law-section:hover h2 a.anchor {
visibility: visible;
}
.social-sharing a {
margin-right: 0.75rem;
font-size: 1.2rem;
color: #555;
text-decoration: none;
}
.social-sharing a:hover {
color: #000;
}
.back-to-top {
margin-top: 1rem;
}
/* Initially hide the hash link */
.header-link {
text-decoration: none;
margin-left: 12px; /* Increased left padding */
opacity: 0;
transition: opacity 0.2s;
font-size: inherit; /* Matches the heading size */
}
/* Only show the hash when the whole section is hovered */
section:hover .header-link,
article:hover .header-link,
div:hover .header-link {
opacity: 1;
}

View File

@@ -1,18 +1,27 @@
# This pipeline builds the PDF ebook on any pull request to master. name: "Validate Pull Request"
name: "Build PDF"
on: on:
pull_request: pull_request:
branches:
- master
jobs: jobs:
prepare-pdf: test-website-build:
# Focal Fossa. Please don't use 'latest' tags, it's an anti-pattern. runs-on: ubuntu-24.04
runs-on: ubuntu-20.04
steps: steps:
# Checkout the code.
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Test Website Build
run: |
cd .github/website
make install
make build
cp -r build/. '../pages'
ls -al "../pages"
prepare-pdf:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
# Set a descriptive version. For PRs it'll be the short sha. # Set a descriptive version. For PRs it'll be the short sha.
- name: Set Version - name: Set Version
@@ -41,3 +50,4 @@ jobs:
with: with:
name: hacker-laws.md name: hacker-laws.md
path: hacker-laws.md path: hacker-laws.md

View File

@@ -30,6 +30,13 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v5
- name: Build Website
run: |
cd .github/website
make install
make build
cp -r build/. '../pages'
ls -al "../pages"
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v3
with: with:

View File

@@ -1,10 +1,14 @@
# 💻📖 hacker-laws <h1 align="center">hacker-laws 💻📖 </h1>
<h4 align="center">🧠 Laws, Theories, Principles and Patterns that developers will find useful.</h4>
Laws, Theories, Principles and Patterns that developers will find useful. ---
[Translations](#translations): [🇮🇩](./translations/id.md) [🇧🇷](./translations/pt-BR.md) [🇨🇳](https://github.com/nusr/hacker-laws-zh) [🇩🇪](./translations/de.md) [🇫🇷](./translations/fr.md) [🇬🇷](./translations/el.md) [🇮🇹](https://github.com/csparpa/hacker-laws-it) [🇱🇻](./translations/lv.md) [🇰🇷](https://github.com/codeanddonuts/hacker-laws-kr) [🇵🇱](./translations/pl.md) [🇷🇺](https://github.com/solarrust/hacker-laws) [🇪🇸](./translations/es-ES.md) [🇹🇷](https://github.com/umutphp/hacker-laws-tr) [🇯🇵](./translations/jp.md) [🇺🇦](./translations/uk.md) [🇻🇳](./translations/vi.md) - 📖 Check out my new book [Effective Shell](https://effective-shell)
- 🌍 Check out the website [hacker-laws.com](https://hacker-laws.com)
Like this project? Please considering [sponsoring me](https://github.com/sponsors/dwmkerr) and the [translators](#translations). Also check out this podcast on [The Changelog - Laws for Hackers to Live By](https://changelog.com/podcast/403) to learn more about the project! You can also [download the latest PDF eBook](https://github.com/dwmkerr/hacker-laws/releases/latest/download/hacker-laws.pdf). Check the [Contributor Guide](./.github/contributing.md) if you are keen to contribute! - ☕️ Like this project? Consider [buying me a coffee with a one-off donation](https://github.com/sponsors/dwmkerr?frequency=one-time)
- 🧠 Check out my new project [Terminal AI](https://github.com/terminal-ai)
- 🎧 Try the podcast [The Changelog - Laws for Hackers to Live By](https://changelog.com/podcast/403)
- 📖 Download the [PDF eBook](https://github.com/dwmkerr/hacker-laws/releases/latest/download/hacker-laws.pd)
--- ---
@@ -95,8 +99,7 @@ There are lots of laws which people discuss when talking about development. This
## Laws ## Laws
And here we go! Laws can be opinions on inevitabilities in the world of software engineering, or wry observations on unavoidable realities.
### 9091 Principle (1% Rule) ### 9091 Principle (1% Rule)

119
assets/site/index.html Normal file
View File

@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactive Hacker Laws Stack</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
margin: 0;
font-family: Arial, sans-serif;
}
#stack {
width: 400px;
height: 500px;
overflow-y: auto;
position: relative;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 10px;
}
.law-item {
text-align: left;
padding: 4px;
transition: transform 0.2s ease, opacity 0.2s;
transform-origin: left;
font-size: 16px;
color: #333;
margin: 6px 0;
position: relative;
display: flex;
align-items: center;
}
.law-item::before {
content: '';
display: block;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #aaa;
margin-right: 8px;
transition: height 0.2s, background-color 0.2s;
}
</style>
</head>
<body>
<div id="stack" class="stack"></div>
<script>
const laws = [
"9091 Principle (1% Rule)", "9090 Rule", "Amdahl's Law", "The Broken Windows Theory",
"Brooks' Law", "CAP Theorem (Brewer's Theorem)", "Clarke's Three Laws", "Conway's Law",
"Cunningham's Law", "Dunbar's Number", "The Dunning-Kruger Effect", "Fitts' Law",
"Gall's Law", "Goodhart's Law", "Hanlon's Razor", "Hofstadter's Law", "Hutber's Law",
"The Hype Cycle & Amara's Law", "Hyrum's Law (The Law of Implicit Interfaces)",
"Metcalfe's Law", "Moore's Law", "Murphy's Law / Sod's Law", "Occam's Razor",
"Parkinson's Law", "Premature Optimization Effect", "Putt's Law", "Reed's Law",
"The Law of Conservation of Complexity (Tesler's Law)", "The Law of Leaky Abstractions",
"The Law of Triviality", "The Unix Philosophy", "The Spotify Model", "Wadler's Law",
"Wheaton's Law", "The Dilbert Principle", "The Pareto Principle (The 80/20 Rule)",
"The Peter Principle", "The Robustness Principle (Postel's Law)", "SOLID",
"The Single Responsibility Principle", "The Open/Closed Principle", "The Liskov Substitution Principle",
"The Interface Segregation Principle", "The Dependency Inversion Principle", "The DRY Principle",
"The KISS Principle", "YAGNI"
];
const stack = document.getElementById('stack');
const maxZoom = 1.5;
const minZoom = 0.6;
laws.forEach(title => {
const div = document.createElement('div');
div.className = 'law-item';
div.innerText = title;
stack.appendChild(div);
});
function updateScale(e) {
const items = document.querySelectorAll('.law-item');
items.forEach(item => {
const itemRect = item.getBoundingClientRect();
const itemCenter = (itemRect.top + itemRect.bottom) / 2;
const distance = Math.abs(e.clientY - itemCenter);
const scale = Math.max(maxZoom - distance / 150, minZoom);
const opacity = Math.max(1 - distance / 300, 0.3);
item.style.transform = `scale(${scale})`;
item.style.opacity = opacity;
const dot = item.querySelector('::before');
const barHeight = Math.max(6, 30 - distance / 10);
item.style.setProperty('--dot-height', barHeight + 'px');
item.style.setProperty('--dot-color', distance < 50 ? '#333' : '#aaa');
item.querySelector('::before');
item.style.setProperty('--dot-height', barHeight + 'px');
});
}
document.addEventListener('mousemove', updateScale);
// Initial positioning
updateScale({ clientY: window.innerHeight / 2 });
</script>
<style>
.law-item::before {
height: var(--dot-height, 6px);
background-color: var(--dot-color, #aaa);
}
</style>
</body>
</html>

110
assets/site/index2.html Normal file
View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactive Hacker Laws Stack</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
margin: 0;
font-family: Arial, sans-serif;
}
#stack {
width: 400px;
height: 500px;
overflow-y: auto;
position: relative;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 10px;
}
.law-item {
text-align: left;
padding: 2px 4px;
transition: transform 0.2s ease, opacity 0.2s;
transform-origin: left;
font-size: 15px;
color: #333;
margin: 4px 0;
position: relative;
display: flex;
align-items: center;
}
.law-item::before {
content: '';
display: block;
width: var(--bar-width, 6px);
height: 6px;
border-radius: 3px;
background-color: var(--bar-color, #aaa);
margin-right: 10px;
transition: width 0.2s, background-color 0.2s;
}
</style>
</head>
<body>
<div id="stack" class="stack"></div>
<script>
const laws = [
"9091 Principle (1% Rule)", "9090 Rule", "Amdahl's Law", "The Broken Windows Theory",
"Brooks' Law", "CAP Theorem (Brewer's Theorem)", "Clarke's Three Laws", "Conway's Law",
"Cunningham's Law", "Dunbar's Number", "The Dunning-Kruger Effect", "Fitts' Law",
"Gall's Law", "Goodhart's Law", "Hanlon's Razor", "Hofstadter's Law", "Hutber's Law",
"The Hype Cycle & Amara's Law", "Hyrum's Law (The Law of Implicit Interfaces)",
"Metcalfe's Law", "Moore's Law", "Murphy's Law / Sod's Law", "Occam's Razor",
"Parkinson's Law", "Premature Optimization Effect", "Putt's Law", "Reed's Law",
"The Law of Conservation of Complexity (Tesler's Law)", "The Law of Leaky Abstractions",
"The Law of Triviality", "The Unix Philosophy", "The Spotify Model", "Wadler's Law",
"Wheaton's Law", "The Dilbert Principle", "The Pareto Principle (The 80/20 Rule)",
"The Peter Principle", "The Robustness Principle (Postel's Law)", "SOLID",
"The Single Responsibility Principle", "The Open/Closed Principle", "The Liskov Substitution Principle",
"The Interface Segregation Principle", "The Dependency Inversion Principle", "The DRY Principle",
"The KISS Principle", "YAGNI"
];
const stack = document.getElementById('stack');
const maxZoom = 1.8;
const minZoom = 0.5;
laws.forEach(title => {
const div = document.createElement('div');
div.className = 'law-item';
div.innerText = title;
stack.appendChild(div);
});
function updateScale(e) {
const items = document.querySelectorAll('.law-item');
items.forEach(item => {
const itemRect = item.getBoundingClientRect();
const itemCenter = (itemRect.top + itemRect.bottom) / 2;
const distance = Math.abs(e.clientY - itemCenter);
const scale = Math.max(maxZoom - distance / 120, minZoom);
const opacity = Math.max(1 - distance / 250, 0.2);
const barWidth = Math.max(6, 60 - distance / 5);
item.style.transform = `scale(${scale})`;
item.style.opacity = opacity;
item.style.setProperty('--bar-width', barWidth + 'px');
item.style.setProperty('--bar-color', distance < 50 ? '#333' : '#aaa');
});
}
document.addEventListener('mousemove', updateScale);
// Initial positioning
updateScale({ clientY: window.innerHeight / 2 });
</script>
</body>
</html>

127
assets/site/index3.html Normal file
View File

@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactive Hacker Laws Stack</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
margin: 0;
font-family: Arial, sans-serif;
overflow: hidden;
}
#stack-container {
width: 400px;
height: 500px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 10px;
overflow: hidden;
position: relative;
}
#stack {
position: absolute;
width: 100%;
}
.law-item {
text-align: left;
padding: 2px 4px;
transition: transform 0.2s ease, opacity 0.2s;
transform-origin: left;
font-size: 15px;
color: #333;
margin: 4px 0;
position: relative;
display: flex;
align-items: center;
}
.law-item::before {
content: '';
display: block;
width: var(--bar-width, 6px);
height: 6px;
border-radius: 3px;
background-color: var(--bar-color, #aaa);
margin-right: 10px;
transition: width 0.2s, background-color 0.2s;
}
</style>
</head>
<body>
<div id="stack-container">
<div id="stack"></div>
</div>
<script>
const laws = [
"9091 Principle (1% Rule)", "9090 Rule", "Amdahl's Law", "The Broken Windows Theory",
"Brooks' Law", "CAP Theorem (Brewer's Theorem)", "Clarke's Three Laws", "Conway's Law",
"Cunningham's Law", "Dunbar's Number", "The Dunning-Kruger Effect", "Fitts' Law",
"Gall's Law", "Goodhart's Law", "Hanlon's Razor", "Hofstadter's Law", "Hutber's Law",
"The Hype Cycle & Amara's Law", "Hyrum's Law (The Law of Implicit Interfaces)",
"Metcalfe's Law", "Moore's Law", "Murphy's Law / Sod's Law", "Occam's Razor",
"Parkinson's Law", "Premature Optimization Effect", "Putt's Law", "Reed's Law",
"The Law of Conservation of Complexity (Tesler's Law)", "The Law of Leaky Abstractions",
"The Law of Triviality", "The Unix Philosophy", "The Spotify Model", "Wadler's Law",
"Wheaton's Law", "The Dilbert Principle", "The Pareto Principle (The 80/20 Rule)",
"The Peter Principle", "The Robustness Principle (Postel's Law)", "SOLID",
"The Single Responsibility Principle", "The Open/Closed Principle", "The Liskov Substitution Principle",
"The Interface Segregation Principle", "The Dependency Inversion Principle", "The DRY Principle",
"The KISS Principle", "YAGNI"
];
const stack = document.getElementById('stack');
const stackContainer = document.getElementById('stack-container');
const maxZoom = 1.8;
const minZoom = 0.5;
let offsetY = 0;
laws.forEach(title => {
const div = document.createElement('div');
div.className = 'law-item';
div.innerText = title;
stack.appendChild(div);
});
function updateScale() {
const items = document.querySelectorAll('.law-item');
items.forEach(item => {
const itemRect = item.getBoundingClientRect();
const containerRect = stackContainer.getBoundingClientRect();
const itemCenter = (itemRect.top + itemRect.bottom) / 2;
const containerCenter = (containerRect.top + containerRect.bottom) / 2;
const distance = Math.abs(containerCenter - itemCenter);
const scale = Math.max(maxZoom - distance / 120, minZoom);
const opacity = Math.max(1 - distance / 300, 0.3);
const barWidth = Math.max(6, 60 - distance / 3);
item.style.transform = `scale(${scale})`;
item.style.opacity = opacity;
item.style.setProperty('--bar-width', barWidth + 'px');
item.style.setProperty('--bar-color', distance < 50 ? '#333' : '#aaa');
});
}
stackContainer.addEventListener('wheel', (e) => {
e.preventDefault();
offsetY = (offsetY + e.deltaY) % stack.scrollHeight;
if (offsetY < 0) offsetY += stack.scrollHeight;
stack.style.top = `${-offsetY}px`;
updateScale();
});
document.addEventListener('mousemove', updateScale);
updateScale();
</script>
</body>
</html>

158
assets/site/index4.html Normal file
View File

@@ -0,0 +1,158 @@
<!--
Interactive Hacker Laws Stack - Specification:
1. User Experience:
- Display a vertically scrolling list of items ("laws") in a visually appealing, interactive format.
- The list infinitely loops seamlessly; scrolling beyond the first or last item cycles continuously.
- Mouse scrolling moves the entire list vertically within a fixed viewport (no internal scrollbar visible).
- The item under the mouse cursor smoothly scales up (zooms) and becomes clearly highlighted.
- Items further from the cursor scale down smoothly, fade out gradually, and become visually less prominent.
- To the left of each text item, a horizontal bar visually represents the focus, forming a bell-curve-like effect.
The bar is smallest (circular) when far from the cursor, and smoothly expands (rectangular with rounded corners) when close.
2. Sources:
- Original Interaction Design Inspiration: https://press.stripe.com/
- Data Source (list of "Hacker Laws"): https://github.com/dwmkerr/hacker-laws/
3. Technical Details:
- Implemented using plain HTML, CSS, and JavaScript without third-party libraries.
- Clearly defined parameters for customization:
- `maxZoom`: maximum scale factor for the item closest to the cursor.
- `minZoom`: minimum scale factor for items furthest from the cursor.
- Styling is clean, minimalist, and customizable via CSS variables.
This specification allows easy adjustments, maintenance, and future enhancements of the interactive list.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactive Hacker Laws Stack</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
margin: 0;
font-family: Arial, sans-serif;
overflow: hidden;
}
#stack-container {
width: 400px;
height: 500px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 10px;
overflow: hidden;
position: relative;
}
#stack {
position: absolute;
width: 100%;
}
.law-item {
text-align: left;
padding: 2px 4px;
transition: transform 0.2s ease, opacity 0.2s;
transform-origin: left;
font-size: 15px;
color: #333;
margin: 4px 0;
position: relative;
display: flex;
align-items: center;
}
.law-item::before {
content: '';
display: block;
width: var(--bar-width, 6px);
height: 6px;
border-radius: 3px;
background-color: var(--bar-color, #aaa);
margin-right: 10px;
transition: width 0.2s, background-color 0.2s;
}
</style>
</head>
<body>
<div id="stack-container">
<div id="stack"></div>
</div>
<script>
const laws = [
"9091 Principle (1% Rule)", "9090 Rule", "Amdahl's Law", "The Broken Windows Theory",
"Brooks' Law", "CAP Theorem (Brewer's Theorem)", "Clarke's Three Laws", "Conway's Law",
"Cunningham's Law", "Dunbar's Number", "The Dunning-Kruger Effect", "Fitts' Law",
"Gall's Law", "Goodhart's Law", "Hanlon's Razor", "Hofstadter's Law", "Hutber's Law",
"The Hype Cycle & Amara's Law", "Hyrum's Law (The Law of Implicit Interfaces)",
"Metcalfe's Law", "Moore's Law", "Murphy's Law / Sod's Law", "Occam's Razor",
"Parkinson's Law", "Premature Optimization Effect", "Putt's Law", "Reed's Law",
"The Law of Conservation of Complexity (Tesler's Law)", "The Law of Leaky Abstractions",
"The Law of Triviality", "The Unix Philosophy", "The Spotify Model", "Wadler's Law",
"Wheaton's Law", "The Dilbert Principle", "The Pareto Principle (The 80/20 Rule)",
"The Peter Principle", "The Robustness Principle (Postel's Law)", "SOLID",
"The Single Responsibility Principle", "The Open/Closed Principle", "The Liskov Substitution Principle",
"The Interface Segregation Principle", "The Dependency Inversion Principle", "The DRY Principle",
"The KISS Principle", "YAGNI"
];
const stack = document.getElementById('stack');
const stackContainer = document.getElementById('stack-container');
const maxZoom = 1.8;
const minZoom = 0.5;
let offsetY = 0;
// Create seamless infinite scrolling
const extendedLaws = [...laws, ...laws, ...laws];
extendedLaws.forEach(title => {
const div = document.createElement('div');
div.className = 'law-item';
div.innerText = title;
stack.appendChild(div);
});
const totalHeight = stack.scrollHeight / 3;
stack.style.top = `-${totalHeight}px`;
offsetY = totalHeight;
function updateScale(e) {
const items = document.querySelectorAll('.law-item');
items.forEach(item => {
const itemRect = item.getBoundingClientRect();
const distance = e.clientY ? Math.abs(e.clientY - (itemRect.top + itemRect.bottom) / 2) : 0;
const scale = Math.max(maxZoom - distance / 120, minZoom);
const opacity = Math.max(1 - distance / 300, 0.3);
const barWidth = Math.max(6, 60 - distance / 3);
item.style.transform = `scale(${scale})`;
item.style.opacity = opacity;
item.style.setProperty('--bar-width', barWidth + 'px');
item.style.setProperty('--bar-color', distance < 50 ? '#333' : '#aaa');
});
}
stackContainer.addEventListener('wheel', (e) => {
e.preventDefault();
offsetY += e.deltaY;
if (offsetY >= totalHeight * 2) offsetY -= totalHeight;
if (offsetY < totalHeight) offsetY += totalHeight;
stack.style.top = `-${offsetY}px`;
updateScale(e);
});
stackContainer.addEventListener('mousemove', updateScale);
// Initial scale update
updateScale({ clientY: window.innerHeight / 2 });
</script>
</body>
</html>