1import { html } from "https://unpkg.com/lit-html?module"; 2import { mount } from "../utils/dom.js"; 3import { skills } from "../../user-data/data.js"; 4 5// Display-only grouping layered on top of the flat `skills` list in user-data/data.js 6// (kept flat there so the JSON generator tool's multi-select keeps working). 7const CATEGORIES = [ 8 { category: "Languages & Frameworks", icon: "fa-solid fa-code", match: ["TypeScript", "Angular", "React", "Signals"] }, 9 { category: "State & Data", icon: "fa-solid fa-diagram-project", match: ["RxJS", "NgRx", "NGXS", "WebSockets"] }, 10 { category: "Architecture", icon: "fa-solid fa-layer-group", match: ["Nx", "Microfrontends", "Module Federation"] }, 11 { category: "Tooling & Quality", icon: "fa-solid fa-vial", match: ["Storybook", "Playwright"] }, 12 { category: "AI", icon: "fa-solid fa-robot", match: ["OpenAI"] }, 13]; 14 15function groupSkills() { 16 const remaining = new Set(skills); 17 const groups = CATEGORIES.map(({ category, icon, match }) => { 18 const items = match.filter((name) => remaining.has(name)); 19 items.forEach((name) => remaining.delete(name)); 20 return { category, icon, items }; 21 }).filter((group) => group.items.length); 22 23 if (remaining.size) { 24 groups.push({ category: "Other", icon: "fa-solid fa-star", items: [...remaining] }); 25 } 26 return groups; 27} 28 29const skillGroup = (group, index) => html` 30 <div class="skill-group glass glass-interactive glow-card" data-reveal style="--reveal-index: ${index}"> 31 <div class="skill-group-head"> 32 <span class="skill-group-icon"><i class="${group.icon}" aria-hidden="true"></i></span> 33 <h3>${group.category}</h3> 34 </div> 35 <ul class="skill-chip-list"> 36 ${group.items.map((item) => html`<li class="chip skill-chip">${item}</li>`)} 37 </ul> 38 </div> 39`; 40 41const skillsTemplate = () => html` 42 <div class="container"> 43 <div class="section-heading"> 44 <p class="section-eyebrow" data-reveal>Toolbox</p> 45 <h2 class="section-title" id="skills-title" data-reveal> 46 Skills & <span class="gradient-text">Technologies</span> 47 </h2> 48 <p class="section-subtitle" data-reveal> 49 The stack I reach for to turn ambiguous requirements into fast, resilient 50 interfaces. 51 </p> 52 </div> 53 <div class="skill-grid"> 54 ${groupSkills().map((group, i) => skillGroup(group, i))} 55 </div> 56 </div> 57`; 58 59export function mountSkills() { 60 return mount("skills", skillsTemplate()); 61}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.