PageSourceSearch

https://vinaysomawat.github.io/src/components/skills.js

js vinaysomawat.github.io collected 2026-10-03 10:08:37 UTC 2,445 bytes, 61 lines download raw bytes

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.