mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 07:36:50 +03:00
Add periodic table visualization and URL hash tracking
- Replace subway map with periodic table of commands - Info panel shows command details on hover - Click element to jump to command in Commands section - URL hash updates as you scroll between sections - Direct linking to sections works - Fix spacing issues in solution section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
6987e6e160
commit
343e342d52
@@ -6,3 +6,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"lenis": "^1.3.16",
|
"lenis": "^1.3.16",
|
||||||
"motion": "^12.23.26",
|
"motion": "^12.23.26",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.1.18",
|
||||||
|
"three": "^0.182.0",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bun-plugin-tailwind": "^0.1.2",
|
"bun-plugin-tailwind": "^0.1.2",
|
||||||
@@ -53,6 +54,8 @@
|
|||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
||||||
|
|
||||||
|
"three": ["three@0.182.0", "", {}, "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ=="],
|
||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -28,6 +28,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lenis": "^1.3.16",
|
"lenis": "^1.3.16",
|
||||||
"motion": "^12.23.26",
|
"motion": "^12.23.26",
|
||||||
"tailwindcss": "^4.1.18"
|
"tailwindcss": "^4.1.18",
|
||||||
|
"three": "^0.182.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -3,9 +3,10 @@ import {
|
|||||||
renderTerminalLayout,
|
renderTerminalLayout,
|
||||||
} from "./js/components/glass-terminal.js";
|
} from "./js/components/glass-terminal.js";
|
||||||
import { initLensEffect } from "./js/components/lens.js";
|
import { initLensEffect } from "./js/components/lens.js";
|
||||||
|
import { initFrameworkViz } from "./js/components/framework-viz.js";
|
||||||
import { initHeroEffect } from "./js/effects/liquid-canvas.js";
|
import { initHeroEffect } from "./js/effects/liquid-canvas.js";
|
||||||
import { initScrollReveal } from "./js/utils/reveal.js";
|
import { initScrollReveal } from "./js/utils/reveal.js";
|
||||||
import { initScrollIndicator, initSmoothScroll } from "./js/utils/scroll.js";
|
import { initScrollIndicator, initSmoothScroll, initHashTracking } from "./js/utils/scroll.js";
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// STATE
|
// STATE
|
||||||
@@ -118,10 +119,12 @@ document.addEventListener("click", (e) => {
|
|||||||
function init() {
|
function init() {
|
||||||
initSmoothScroll();
|
initSmoothScroll();
|
||||||
initScrollIndicator();
|
initScrollIndicator();
|
||||||
|
initHashTracking();
|
||||||
initHeroEffect();
|
initHeroEffect();
|
||||||
initLensEffect();
|
initLensEffect();
|
||||||
initScrollReveal();
|
initScrollReveal();
|
||||||
initGlassTerminal();
|
initGlassTerminal();
|
||||||
|
initFrameworkViz();
|
||||||
loadContent();
|
loadContent();
|
||||||
|
|
||||||
document.body.classList.add("loaded");
|
document.body.classList.add("loaded");
|
||||||
|
|||||||
+16
-1
@@ -647,7 +647,11 @@ code {
|
|||||||
|
|
||||||
.solution-content {
|
.solution-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacing-xl);
|
gap: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-content .section-lead {
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Two-pillar visual layout */
|
/* Two-pillar visual layout */
|
||||||
@@ -665,6 +669,17 @@ code {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Interactive Framework Viz - Periodic Table */
|
||||||
|
.solution-visual-interactive {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 380px;
|
||||||
|
background: var(--color-paper);
|
||||||
|
border: 1px solid var(--color-mist);
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.solution-pillar {
|
.solution-pillar {
|
||||||
background: var(--color-cream);
|
background: var(--color-cream);
|
||||||
border: 1px solid var(--color-mist);
|
border: 1px solid var(--color-mist);
|
||||||
|
|||||||
+4
-75
@@ -15,7 +15,8 @@
|
|||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"lenis": "https://cdn.jsdelivr.net/npm/lenis@1.3.16/+esm",
|
"lenis": "https://cdn.jsdelivr.net/npm/lenis@1.3.16/+esm",
|
||||||
"motion": "https://cdn.jsdelivr.net/npm/motion@12.23.26/+esm"
|
"motion": "https://cdn.jsdelivr.net/npm/motion@12.23.26/+esm",
|
||||||
|
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -180,80 +181,8 @@
|
|||||||
<div class="solution-content">
|
<div class="solution-content">
|
||||||
<p class="section-lead" data-reveal>One comprehensive skill with deep expertise, plus 15 commands that form the language of design.</p>
|
<p class="section-lead" data-reveal>One comprehensive skill with deep expertise, plus 15 commands that form the language of design.</p>
|
||||||
|
|
||||||
<div class="solution-visual" data-reveal>
|
<div class="solution-visual-interactive" id="framework-viz-container" data-reveal>
|
||||||
<!-- Skills Column -->
|
<!-- Subway map generated by JS -->
|
||||||
<div class="solution-pillar solution-pillar--skills">
|
|
||||||
<div class="pillar-header">
|
|
||||||
<span class="pillar-icon">
|
|
||||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
||||||
<path d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<h3 class="pillar-title">One Skill</h3>
|
|
||||||
<p class="pillar-subtitle">Infinite expertise, progressive loading</p>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-content">
|
|
||||||
<div class="pillar-item pillar-item--main">
|
|
||||||
<span class="pillar-item-name">Frontend Design</span>
|
|
||||||
<span class="pillar-item-desc">Comprehensive design intelligence</span>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-item pillar-item--ref">
|
|
||||||
<span class="pillar-item-label">Reference domains:</span>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-refs">
|
|
||||||
<span class="pillar-ref">Typography</span>
|
|
||||||
<span class="pillar-ref">Color</span>
|
|
||||||
<span class="pillar-ref">Spatial</span>
|
|
||||||
<span class="pillar-ref">Responsive</span>
|
|
||||||
<span class="pillar-ref">Interaction</span>
|
|
||||||
<span class="pillar-ref">Motion</span>
|
|
||||||
<span class="pillar-ref">UX Writing</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Connector -->
|
|
||||||
<div class="solution-connector">
|
|
||||||
<span class="connector-plus">+</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Commands Column -->
|
|
||||||
<div class="solution-pillar solution-pillar--commands">
|
|
||||||
<div class="pillar-header">
|
|
||||||
<span class="pillar-icon">
|
|
||||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
||||||
<path d="M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z"/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<h3 class="pillar-title">15 Commands</h3>
|
|
||||||
<p class="pillar-subtitle">The language of design</p>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-content">
|
|
||||||
<div class="pillar-command-group">
|
|
||||||
<span class="pillar-group-label">Diagnose</span>
|
|
||||||
<code class="pillar-item-code">/audit</code>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-command-group">
|
|
||||||
<span class="pillar-group-label">Calibrate</span>
|
|
||||||
<code class="pillar-item-code">/bolder</code>
|
|
||||||
<code class="pillar-item-code">/quieter</code>
|
|
||||||
<code class="pillar-item-code">/simplify</code>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-command-group">
|
|
||||||
<span class="pillar-group-label">Enhance</span>
|
|
||||||
<code class="pillar-item-code">/animate</code>
|
|
||||||
<code class="pillar-item-code">/colorize</code>
|
|
||||||
<code class="pillar-item-code">/delight</code>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-command-group">
|
|
||||||
<span class="pillar-group-label">Align</span>
|
|
||||||
<code class="pillar-item-code">/normalize</code>
|
|
||||||
<code class="pillar-item-code">/polish</code>
|
|
||||||
<code class="pillar-item-code">/optimize</code>
|
|
||||||
</div>
|
|
||||||
<div class="pillar-item pillar-item--more">+5 more commands</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="solution-result" data-reveal>
|
<div class="solution-result" data-reveal>
|
||||||
|
|||||||
@@ -131,3 +131,5 @@ function setupInteractions() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,357 @@
|
|||||||
|
/**
|
||||||
|
* Periodic Table of Commands
|
||||||
|
* Clean grid visualization showing all commands organized by category
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { commandCategories, commandRelationships } from '../data.js';
|
||||||
|
|
||||||
|
const categoryColors = {
|
||||||
|
diagnostic: { bg: '#fdf4ff', border: '#d946ef', text: '#a21caf' },
|
||||||
|
quality: { bg: '#f0fdf4', border: '#22c55e', text: '#15803d' },
|
||||||
|
intensity: { bg: '#fffbeb', border: '#f59e0b', text: '#b45309' },
|
||||||
|
adaptation: { bg: '#eff6ff', border: '#3b82f6', text: '#1d4ed8' },
|
||||||
|
enhancement: { bg: '#fdf2f8', border: '#ec4899', text: '#be185d' },
|
||||||
|
system: { bg: '#f9fafb', border: '#6b7280', text: '#374151' }
|
||||||
|
};
|
||||||
|
|
||||||
|
const categoryLabels = {
|
||||||
|
diagnostic: 'Diagnostic',
|
||||||
|
quality: 'Quality',
|
||||||
|
intensity: 'Intensity',
|
||||||
|
adaptation: 'Adaptation',
|
||||||
|
enhancement: 'Enhancement',
|
||||||
|
system: 'System'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Short symbols for each command (like element symbols)
|
||||||
|
const commandSymbols = {
|
||||||
|
audit: 'Au',
|
||||||
|
normalize: 'No',
|
||||||
|
polish: 'Po',
|
||||||
|
optimize: 'Op',
|
||||||
|
harden: 'Ha',
|
||||||
|
clarify: 'Cl',
|
||||||
|
simplify: 'Si',
|
||||||
|
adapt: 'Ad',
|
||||||
|
extract: 'Ex',
|
||||||
|
animate: 'An',
|
||||||
|
colorize: 'Co',
|
||||||
|
delight: 'De',
|
||||||
|
bolder: 'Bo',
|
||||||
|
quieter: 'Qu',
|
||||||
|
onboard: 'On'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Atomic numbers (just for visual interest)
|
||||||
|
const commandNumbers = {
|
||||||
|
audit: 1,
|
||||||
|
normalize: 2,
|
||||||
|
polish: 3,
|
||||||
|
optimize: 4,
|
||||||
|
harden: 5,
|
||||||
|
clarify: 6,
|
||||||
|
simplify: 7,
|
||||||
|
adapt: 8,
|
||||||
|
extract: 9,
|
||||||
|
animate: 10,
|
||||||
|
colorize: 11,
|
||||||
|
delight: 12,
|
||||||
|
bolder: 13,
|
||||||
|
quieter: 14,
|
||||||
|
onboard: 15
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PeriodicTable {
|
||||||
|
constructor(container) {
|
||||||
|
this.container = container;
|
||||||
|
this.infoPanel = null;
|
||||||
|
this.activeElement = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.container.innerHTML = '';
|
||||||
|
this.container.style.cssText = `
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.renderTable();
|
||||||
|
this.renderInfoPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTable() {
|
||||||
|
// Group commands by category
|
||||||
|
const groups = {};
|
||||||
|
Object.entries(commandCategories).forEach(([cmd, cat]) => {
|
||||||
|
if (!groups[cat]) groups[cat] = [];
|
||||||
|
groups[cat].push(cmd);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Category order
|
||||||
|
const categoryOrder = ['diagnostic', 'quality', 'adaptation', 'enhancement', 'intensity', 'system'];
|
||||||
|
|
||||||
|
// Create grid container
|
||||||
|
const grid = document.createElement('div');
|
||||||
|
grid.style.cssText = `
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
categoryOrder.forEach(cat => {
|
||||||
|
const commands = groups[cat];
|
||||||
|
if (!commands) return;
|
||||||
|
|
||||||
|
const group = this.createCategoryGroup(cat, commands);
|
||||||
|
grid.appendChild(group);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.appendChild(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInfoPanel() {
|
||||||
|
this.infoPanel = document.createElement('div');
|
||||||
|
this.infoPanel.style.cssText = `
|
||||||
|
background: var(--color-cream);
|
||||||
|
border: 1px solid var(--color-mist);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
height: 96px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Default state
|
||||||
|
this.showDefaultInfo();
|
||||||
|
|
||||||
|
this.container.appendChild(this.infoPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
showDefaultInfo() {
|
||||||
|
this.infoPanel.innerHTML = `
|
||||||
|
<div style="
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-ash);
|
||||||
|
font-style: italic;
|
||||||
|
">
|
||||||
|
Hover over a command to see details
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
showCommandInfo(cmd) {
|
||||||
|
const category = commandCategories[cmd];
|
||||||
|
const colors = categoryColors[category];
|
||||||
|
const rel = commandRelationships[cmd] || {};
|
||||||
|
|
||||||
|
// Normalize to arrays (pairs can be string or array)
|
||||||
|
const toArray = (val) => {
|
||||||
|
if (!val) return [];
|
||||||
|
if (Array.isArray(val)) return val;
|
||||||
|
return [val];
|
||||||
|
};
|
||||||
|
|
||||||
|
const pairs = toArray(rel.pairs);
|
||||||
|
const leadsTo = toArray(rel.leadsTo);
|
||||||
|
const combinesWith = toArray(rel.combinesWith);
|
||||||
|
|
||||||
|
// Build related info
|
||||||
|
let relatedHtml = '';
|
||||||
|
|
||||||
|
if (pairs.length > 0) {
|
||||||
|
const list = pairs.map(p => `<span style="font-family: var(--font-mono); color: var(--color-ink)">/${p}</span>`).join(', ');
|
||||||
|
relatedHtml += `<span style="color: var(--color-ash)">Pairs with:</span> ${list}`;
|
||||||
|
}
|
||||||
|
if (combinesWith.length > 0) {
|
||||||
|
const list = combinesWith.map(p => `<span style="font-family: var(--font-mono); color: var(--color-ink)">/${p}</span>`).join(', ');
|
||||||
|
if (relatedHtml) relatedHtml += '<span style="color: var(--color-mist); margin: 0 8px;">•</span>';
|
||||||
|
relatedHtml += `<span style="color: var(--color-ash)">Combines with:</span> ${list}`;
|
||||||
|
}
|
||||||
|
if (leadsTo.length > 0) {
|
||||||
|
const list = leadsTo.map(p => `<span style="font-family: var(--font-mono); color: var(--color-ink)">/${p}</span>`).join(', ');
|
||||||
|
if (relatedHtml) relatedHtml += '<span style="color: var(--color-mist); margin: 0 8px;">•</span>';
|
||||||
|
relatedHtml += `<span style="color: var(--color-ash)">Then:</span> ${list}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.infoPanel.innerHTML = `
|
||||||
|
<div style="flex: 1; min-width: 0;">
|
||||||
|
<div style="
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
">
|
||||||
|
<span style="
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-ink);
|
||||||
|
">/${cmd}</span>
|
||||||
|
<span style="
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: ${colors.text};
|
||||||
|
font-weight: 500;
|
||||||
|
">${categoryLabels[category]}</span>
|
||||||
|
</div>
|
||||||
|
<div style="
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-charcoal);
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: ${relatedHtml ? '6px' : '0'};
|
||||||
|
">${rel.flow || 'Design command'}</div>
|
||||||
|
${relatedHtml ? `<div style="font-size: 12px; line-height: 1.4;">${relatedHtml}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
createCategoryGroup(category, commands) {
|
||||||
|
const colors = categoryColors[category];
|
||||||
|
|
||||||
|
const group = document.createElement('div');
|
||||||
|
group.style.cssText = `
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Category label
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.style.cssText = `
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: ${colors.text};
|
||||||
|
padding-left: 2px;
|
||||||
|
`;
|
||||||
|
label.textContent = categoryLabels[category];
|
||||||
|
group.appendChild(label);
|
||||||
|
|
||||||
|
// Elements row
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.style.cssText = `
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
commands.forEach(cmd => {
|
||||||
|
const element = this.createElement(cmd, category);
|
||||||
|
row.appendChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
group.appendChild(row);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
createElement(cmd, category) {
|
||||||
|
const colors = categoryColors[category];
|
||||||
|
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.style.cssText = `
|
||||||
|
width: 56px;
|
||||||
|
height: 64px;
|
||||||
|
background: ${colors.bg};
|
||||||
|
border: 1.5px solid ${colors.border};
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Atomic number
|
||||||
|
const number = document.createElement('div');
|
||||||
|
number.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 5px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 7px;
|
||||||
|
color: ${colors.text};
|
||||||
|
opacity: 0.5;
|
||||||
|
`;
|
||||||
|
number.textContent = commandNumbers[cmd];
|
||||||
|
el.appendChild(number);
|
||||||
|
|
||||||
|
// Symbol
|
||||||
|
const symbol = document.createElement('div');
|
||||||
|
symbol.style.cssText = `
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: ${colors.text};
|
||||||
|
line-height: 1;
|
||||||
|
`;
|
||||||
|
symbol.textContent = commandSymbols[cmd];
|
||||||
|
el.appendChild(symbol);
|
||||||
|
|
||||||
|
// Command name
|
||||||
|
const name = document.createElement('div');
|
||||||
|
name.style.cssText = `
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 8px;
|
||||||
|
color: ${colors.text};
|
||||||
|
opacity: 0.7;
|
||||||
|
margin-top: 3px;
|
||||||
|
`;
|
||||||
|
name.textContent = `/${cmd}`;
|
||||||
|
el.appendChild(name);
|
||||||
|
|
||||||
|
// Hover effects
|
||||||
|
el.addEventListener('mouseenter', () => {
|
||||||
|
// Visual feedback
|
||||||
|
el.style.transform = 'translateY(-2px)';
|
||||||
|
el.style.boxShadow = `0 4px 12px ${colors.border}40`;
|
||||||
|
|
||||||
|
// Update info panel
|
||||||
|
this.showCommandInfo(cmd);
|
||||||
|
|
||||||
|
// Track active element
|
||||||
|
if (this.activeElement) {
|
||||||
|
this.activeElement.style.transform = 'translateY(0)';
|
||||||
|
this.activeElement.style.boxShadow = 'none';
|
||||||
|
}
|
||||||
|
this.activeElement = el;
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener('mouseleave', () => {
|
||||||
|
el.style.transform = 'translateY(0)';
|
||||||
|
el.style.boxShadow = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click to scroll to command
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const target = document.getElementById(`cmd-${cmd}`);
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initFrameworkViz() {
|
||||||
|
const container = document.getElementById('framework-viz-container');
|
||||||
|
if (container) {
|
||||||
|
new PeriodicTable(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -181,3 +181,5 @@ function updateTerminal(cmd, container, allCommands) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,3 +11,5 @@ export function initLensEffect() {
|
|||||||
maxPosition: 95
|
maxPosition: 95
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -125,3 +125,5 @@ export function setupDemoTabs() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,3 +39,5 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,3 +37,5 @@ export const commandDemos = {
|
|||||||
export function getCommandDemo(commandId) {
|
export function getCommandDemo(commandId) {
|
||||||
return commandDemos[commandId] || null;
|
return commandDemos[commandId] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,3 +37,5 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
// Main demos registry - exports all demos
|
// Main demos registry - exports all demos
|
||||||
export { commandDemos, getCommandDemo } from "./commands/index.js";
|
export { commandDemos, getCommandDemo } from "./commands/index.js";
|
||||||
export { getSkillDemo, skillDemos } from "./skills/index.js";
|
export { getSkillDemo, skillDemos } from "./skills/index.js";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -63,3 +63,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,3 +21,5 @@ export const skillDemos = {
|
|||||||
export function getSkillDemo(skillId) {
|
export function getSkillDemo(skillId) {
|
||||||
return skillDemos[skillId] || null;
|
return skillDemos[skillId] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,3 +74,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,3 +64,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -86,3 +86,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,3 +57,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,3 +33,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -66,3 +66,5 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -154,3 +154,5 @@ export function initHeroEffect() {
|
|||||||
|
|
||||||
observer.observe(canvas);
|
observer.observe(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,3 +13,5 @@ export function initScrollReveal() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,3 +47,56 @@ export function initScrollIndicator() {
|
|||||||
window.addEventListener("scroll", update, { passive: true });
|
window.addEventListener("scroll", update, { passive: true });
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function initHashTracking() {
|
||||||
|
const sections = document.querySelectorAll('section[id]');
|
||||||
|
if (!sections.length) return;
|
||||||
|
|
||||||
|
let currentHash = window.location.hash.slice(1) || '';
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function updateHash() {
|
||||||
|
const scrollY = window.scrollY;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
const triggerPoint = scrollY + viewportHeight * 0.3;
|
||||||
|
|
||||||
|
let activeSection = '';
|
||||||
|
|
||||||
|
sections.forEach(section => {
|
||||||
|
const rect = section.getBoundingClientRect();
|
||||||
|
const sectionTop = scrollY + rect.top;
|
||||||
|
const sectionBottom = sectionTop + rect.height;
|
||||||
|
|
||||||
|
if (triggerPoint >= sectionTop && triggerPoint < sectionBottom) {
|
||||||
|
activeSection = section.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activeSection && activeSection !== currentHash) {
|
||||||
|
currentHash = activeSection;
|
||||||
|
history.replaceState(null, '', `#${activeSection}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(updateHash);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Handle initial hash on page load
|
||||||
|
if (window.location.hash) {
|
||||||
|
const target = document.querySelector(window.location.hash);
|
||||||
|
if (target) {
|
||||||
|
setTimeout(() => {
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial check
|
||||||
|
updateHash();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user