๐Ÿ“‹ 30 Skills ๐Ÿ“ฆ 723 KB ๐Ÿท๏ธ 3 Categories

๐Ÿค– Skill Assistant

Hi! I'm your AI assistant for these mastery skills. Ask me anything about the content โ€” commands, patterns, best practices, or architecture decisions. I have full knowledge of all 30 skills.
\n\n\n Skip to main content\n
\n \n
\n
\n
\n

Primary Heading

\n
\n

Section Heading

\n
\n
\n \n
\n \n\n\n```\n\n### ARIA Rules \u2014 When & When NOT to Use\n\n| Rule | Correct | Wrong |\n|------|---------|-------|\n| First rule | Use native HTML element | Add ARIA to `
` |\n| Labels | `aria-labelledby` on interactive element | `aria-label` on `` |\n| Live regions | `aria-live=\"polite\"` for updates | `role=\"alert\"` everywhere |\n| Hidden | `aria-hidden=\"true\"` on decorative SVG | `display:none` + ARIA role |\n| Disabled | HTML `disabled` attribute | `aria-disabled` on `\n\n\n
X
\n\n\n
\n \n \n \n
\n```\n\n### Client-Side Form Validation (Security + UX)\n\n```js\n// Harden server-side; client is convenience only\ndocument.querySelector('form').addEventListener('submit', (e) => {\n const input = document.getElementById('email');\n const error = document.getElementById('email-error');\n if (!input.checkValidity()) {\n e.preventDefault();\n input.setAttribute('aria-invalid', 'true');\n error.textContent = input.validationMessage; // browser-native message\n }\n});\n\n// Sanitize on input to prevent XSS in error displays\ninput.addEventListener('input', () => {\n input.setAttribute('aria-invalid', 'false');\n error.textContent = '';\n // Strip any HTML if dynamically setting error text\n});\n```\n\n---\n\n## 2. CSS Architecture \u2014 BEM + ITCSS + Cascade Layers\n\n### Custom Properties & Design Tokens\n\n```css\n/* @layer order declaration \u2014 lowest priority first */\n@layer reset, base, layout, components, utilities;\n\n/* \u2500\u2500 Settings (Layer: base) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n:root {\n /* Spacing scale (8px base, clamp for fluid) */\n --space-xs: clamp(4px, 0.5vw, 8px);\n --space-s: clamp(8px, 1vw, 16px);\n --space-m: clamp(16px, 2vw, 24px);\n --space-l: clamp(24px, 3vw, 40px);\n --space-xl: clamp(40px, 5vw, 64px);\n\n /* Type scale with fluid clamp */\n --fs-body: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);\n --fs-h1: clamp(2rem, 1.5rem + 2.5vw, 3.5rem);\n --line-height: 1.5;\n\n /* Color tokens (avoid light/dark in name) */\n --color-bg: #ffffff;\n --color-text: #1a1a1a;\n --color-primary: #2563eb;\n --color-danger: #dc2626;\n --color-border: #e5e7eb;\n\n /* Container widths */\n --container-s: 40rem;\n --container-m: 60rem;\n --container-l: 80rem;\n}\n\n/* Dark mode via preference + data attribute */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --color-bg: #0f172a;\n --color-text: #e2e8f0;\n --color-border: #334155;\n }\n}\n[data-theme=\"dark\"] {\n --color-bg: #0f172a;\n --color-text: #e2e8f0;\n --color-border: #334155;\n}\n```\n\n### BEM Naming with Container Queries\n\n```css\n/* \u2500\u2500 Component Layer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n@layer components {\n\n.card { /* Block */\n container-type: inline-size;\n container-name: card;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 0.5rem;\n padding: var(--space-m);\n}\n .card__image { /* Element */\n width: 100%;\n aspect-ratio: 16 / 9;\n object-fit: cover;\n }\n .card__title { /* Element */\n font-size: var(--fs-body);\n margin-block-start: var(--space-s);\n }\n .card__action { /* Element */\n margin-block-start: var(--space-s);\n }\n .card--featured .card__title {/* Modifier */\n font-size: var(--fs-h1);\n }\n\n/* Container query: context-aware responsive */\n@container card (min-width: 400px) {\n .card {\n display: grid;\n grid-template: \"image title\" auto\n \"image action\" 1fr / 200px 1fr;\n gap: var(--space-m);\n }\n .card__image { grid-area: image; }\n .card__title { grid-area: title; }\n .card__action { grid-area: action; align-self: end; }\n}\n} /* end components layer */\n```\n\n### Utility Layer\n\n```css\n@layer utilities {\n .u-visually-hidden {\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 1px;\n overflow: hidden;\n position: absolute;\n white-space: nowrap;\n width: 1px;\n }\n .u-flow > * + * { margin-block-start: var(--space-m); }\n}\n```\n\n---\n\n## 3. Responsive Design \u2014 Mobile-First + Logical Properties\n\n### Breakpoint Decision Tree\n\n```mermaid\nflowchart TD\n A[\"Design component\"] --> B{\"Needs layout change?\"}\n B -- No --> C[\"Fluid: clamp(), %, vw\"]\n B -- Yes --> D{\"Change at viewport
or container context?\"}\n D -- Container --> E[\"@container query\"]\n D -- Viewport --> F{\"Breakpoint needed?\"}\n F -- \"640px+\" --> G[\"@media \u226540em
(small tablet)\"]\n F -- \"1024px+\" --> H[\"@media \u226564em
(desktop)\"]\n F -- \"1280px+\" --> I[\"@media \u226580em
(wide)\"]\n E --> J[\"Set container-type on parent\"]\n G --> K[\"Mobile-first: min-width\"]\n H --> K\n I --> K\n```\n\n### Mobile-First Flexbox \u2192 Grid\n\n```css\n/* \u2500\u2500 Layout Layer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n@layer layout {\n .page {\n display: flex;\n flex-direction: column;\n min-block-size: 100dvh; /* dynamic viewport \u2014 avoids mobile toolbar */\n padding-inline: var(--space-s);\n }\n\n /* Stack by default, grid on wider viewports */\n .grid {\n display: grid;\n gap: var(--space-m);\n grid-template-columns: 1fr; /* mobile: single column */\n }\n\n @media (min-width: 40em) { /* 640px \u2014 small tablet */\n .grid { grid-template-columns: repeat(2, 1fr); }\n }\n @media (min-width: 64em) { /* 1024px \u2014 desktop */\n .grid { grid-template-columns: repeat(3, 1fr); }\n }\n\n /* Holy Grail layout */\n .holy-grail {\n display: grid;\n grid-template:\n \"header\" auto\n \"main\" 1fr\n \"footer\" auto / 1fr;\n min-block-size: 100dvh;\n }\n @media (min-width: 64em) {\n .holy-grail {\n grid-template:\n \"header header\" auto\n \"nav main\" 1fr\n \"footer footer\" auto / 15rem 1fr;\n }\n }\n\n /* Logical properties for RTL support */\n .card {\n padding-block: var(--space-m);\n padding-inline: var(--space-s);\n border-inline-start: 4px solid var(--color-primary);\n margin-block-end: var(--space-m);\n inset-inline-start: 0;\n }\n}\n```\n\n### Fluid Typography & Spacing with `clamp()`\n\n```css\n/* No breakpoint needed \u2014 continuous scaling */\nh1 {\n font-size: clamp(1.8rem, 1.2rem + 2.5vw, 3.5rem);\n line-height: 1.1;\n text-wrap: balance; /* CSS Text Level 4 */\n}\np {\n max-inline-size: 65ch; /* optimal reading width */\n text-wrap: pretty; /* prevent orphans */\n}\n```\n\n---\n\n## 4. Performance \u2014 Critical CSS, Fonts, Core Web Vitals\n\n### Loading & Rendering Pipeline\n\n```mermaid\nflowchart LR\n A[\"HTML request\"] --> B[\"Parse & build DOM\"]\n B --> C{\" CSS?\"}\n C -- Render-blocking --> D[\"Fetch + parse CSS \u2192 CSSOM\"]\n C -- \"rel='preload'
async\" --> E[\"Non-blocking fetch\"]\n D --> F[\"DOM + CSSOM \u2192 Render Tree\"]\n E --> F\n F --> G[\"Layout \u2192 Paint \u2192 Composite\"]\n B -->|\"\"| H[\"Preload critical font\"]\n H --> I[\"Font Display: swap \u2192 FOUT\"]\n subgraph CoreWebVitals[\"Core Web Vitals Targets\"]\n LCP[\"LCP < 2.5s\"]\n INP[\"INP < 200ms\"]\n CLS[\"CLS < 0.1\"]\n end\n G -.-> LCP\n I -.-> CLS\n```\n\n### Critical CSS Inlining\n\n```html\n\n \n \n \n \n \n\n```\n\n### Font Loading Strategy\n\n```css\n/* Use font-display + preconnect for speed */\n@font-face {\n font-family: \"Brand Sans\";\n src: url(\"/fonts/brand-sans-var.woff2\") format(\"woff2\");\n font-weight: 100 900; /* variable font range */\n font-style: normal;\n font-display: swap; /* FOUT, not FOIT \u2192 avoids CLS */\n unicode-range: U+0020-007E; /* subset: basic Latin only */\n}\n```\n\n```html\n\n\n\n\n\n```\n\n### Image Optimization + LCP Fix\n\n```html\n\n \n \n \n \"Descriptive\n fetchpriority=\"high\" \n decoding=\"async\"\n loading=\"eager\"> \n\n```\n\n```css\n/* Avoid layout shift from aspect-ratio mismatch */\nimg {\n max-inline-size: 100%;\n block-size: auto;\n aspect-ratio: 16 / 9; /* matches width/height attr */\n background: var(--color-border); /* skeleton while loading */\n}\n```\n\n### CSS Containment for Render Performance\n\n```css\n/* Isolate component subtrees from global layout recalc */\n.card { contain: layout style; }\n.sidebar-widget { contain: content; } /* layout + style + paint */\n.hero-background { content-visibility: auto;\n contain-intrinsic-size: auto 50dvh; }\n```\n\n---\n\n## 5. Security \u2014 CSP, XSS Prevention, SVG Sanitization\n\n### Content Security Policy (CSP)\n\n```html\n\n\n```\n\n> **Key rule:** With CSP, avoid `style-src 'unsafe-inline'`. Use CSS custom properties and classes instead of inline `style=\"\"` attributes. If inline styles are unavoidable, use a nonce: `style-src 'nonce-abc123'` and `

${title}

Category: ${CATS[currentSkill.category]?.label || currentSkill.category} | Size: ${(currentSkill.size/1024).toFixed(1)} KB


${content}
BusinessExplain Mastery Portal โ€” businessexplain.hkus2.s4s.host
`); printWindow.document.close(); } // โ”€โ”€โ”€ COPY ALL โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function copyAll() { if (!currentSkill) return; navigator.clipboard.writeText(currentSkill.content).then(() => { const btn = document.querySelector('.actions .btn-secondary'); btn.textContent = 'โœ… Copied!'; setTimeout(() => btn.textContent = '๐Ÿ“‹ Copy Content', 2000); }); } // โ”€โ”€โ”€ ASK ABOUT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function askAbout() { if (!currentSkill) return; const chatPanel = document.getElementById('chatPanel'); if (!chatPanel.classList.contains('open')) toggleChat(); const input = document.getElementById('chatInput'); input.value = `Tell me the key takeaways and best practices from the ${currentSkill.name} skill`; input.focus(); } // โ”€โ”€โ”€ CHAT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function toggleChat() { document.getElementById('chatPanel').classList.toggle('open'); } let chatHistory = []; async function sendChat() { const input = document.getElementById('chatInput'); const msg = input.value.trim(); if (!msg) return; input.value = ''; // Add user message addChatMsg('user', msg); // Build context from relevant skills const context = buildChatContext(msg); // Try OpenClaw API first, then fallback to local response try { const response = await fetch('/api/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({message: msg, context: context}) }); const data = await response.json(); addChatMsg('assistant', data.reply || 'Sorry, I could not generate a response.'); } catch(e) { addChatMsg('assistant', generateLocalReply(msg)); } } function buildChatContext(msg) { const words = msg.toLowerCase().split(/\s+/); let relevant = SKILLS.map(s => { let score = 0; for (const w of words) { if (s.name.toLowerCase().includes(w)) score += 10; if (s.description.toLowerCase().includes(w)) score += 5; if (s.content.toLowerCase().includes(w)) score += 1; } return {...s, score}; }).filter(s => s.score > 0).sort((a,b) => b.score - a.score).slice(0, 3); if (relevant.length === 0) relevant = SKILLS.slice(0, 2); return relevant.map(s => `=== ${s.name} (excerpt) ===\n${s.content.slice(0, 2000)}`).join('\n\n'); } function generateLocalReply(msg) { const m = msg.toLowerCase(); // Find most relevant skill let best = null, bestScore = 0; for (const s of SKILLS) { let score = 0; const words = m.split(/\s+/); for (const w of words) { if (s.name.includes(w)) score += 10; if (s.description.toLowerCase().includes(w)) score += 5; } if (score > bestScore) { bestScore = score; best = s; } } if (best && bestScore > 5) { return `Based on the **${best.name}** mastery guide, here are the key points:\n\n${best.description}\n\nI can see this skill covers ${(best.size/1024).toFixed(1)} KB of detailed content. Click on it in the sidebar to read the full guide, or ask me a more specific question!`; } if (m.includes('hello') || m.includes('hi')) { return 'Hello! ๐Ÿ‘‹ I have access to all 30 mastery skills on this portal. Ask me about any technology, pattern, or best practice โ€” from Kubernetes to Python to AI agents!'; } if (m.includes('how many') || m.includes('list') || m.includes('categories')) { return `This portal contains **${SKILLS.length} mastery skills** across 3 categories:\n\n๐Ÿค– **AI Agents** (3 skills): hermes-agent-mastery, openclaw-mastery, ai-agent-mastery\n๐Ÿ’ป **Coding & Engineering** (8 skills): python, react, nextjs, php, html-css, mobile, lemp-lamp, secure-coding\nโš™๏ธ **DevOps & Infrastructure** (19 skills): kubernetes, terraform, CI/CD, monitoring, databases, networking, and more`; } return `I can help you explore our 30 mastery skills. Try asking about a specific technology like "Kubernetes", "Python patterns", or "AI agent architecture". You can also click any skill in the sidebar to read the full guide!`; } function addChatMsg(role, text) { const container = document.getElementById('chatMessages'); const div = document.createElement('div'); div.className = `chat-msg ${role}`; div.innerHTML = `
${mdToHtml(text)}
`; container.appendChild(div); container.scrollTop = container.scrollHeight; } // โ”€โ”€โ”€ UTILITIES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function groupBy(arr, key) { return arr.reduce((acc, item) => { (acc[item[key]] = acc[item[key]] || []).push(item); return acc; }, {}); } function escAttr(s) { return s.replace(/"/g, '"').replace(/'/g, '''); } function scrollToSection(name) { const el = document.getElementById('sec-' + encodeURIComponent(name)); if (el) el.scrollIntoView({behavior:'smooth'}); } function copyCode(btn) { const code = btn.closest('.code-block').querySelector('code').textContent; navigator.clipboard.writeText(code).then(() => { btn.textContent = 'โœ“ Copied'; setTimeout(() => btn.textContent = 'Copy', 2000); }); } // โ”€โ”€โ”€ INIT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ showLanding();