Hero subtitle and blurb rewritten to lead with security operations and homelab credentials over generic "builder" framing. Projects: archive plAIground, service-monitor, ThoughtSpace (2025); add open-pact and helm as featured; add risk-ops to archive (2026). Add statusBadge + externalUrl to Project type; wire golf-book-mobile. Services: 24 → 37 — split grouped arr/media entries, add mail relay, gluetun, Home Assistant, Glance, Filebrowser, Prowlarr, Bazarr, nzbget, qBittorrent, Kavita, Openshelf. Drop Calibre-Web. Skills: add Go to Languages. Timeline: update monitoring stack. Homelab ADRs: add Authentik over Authelia.
49 lines
1.3 KiB
TypeScript
49 lines
1.3 KiB
TypeScript
import Widget from "@/components/Widget";
|
|
|
|
const skillGroups = [
|
|
{
|
|
label: "Languages",
|
|
skills: ["Go", "JavaScript", "TypeScript", "HTML", "CSS"],
|
|
},
|
|
{
|
|
label: "Frontend",
|
|
skills: ["React", "React Native", "Expo", "Next.js", "Three.js"],
|
|
},
|
|
{
|
|
label: "Desktop & Tools",
|
|
skills: ["Electron", "Node.js", "REST APIs", "Git", "Docker", "TDD"],
|
|
},
|
|
{
|
|
label: "Infrastructure",
|
|
skills: ["Proxmox", "pfSense", "VLANs", "WireGuard", "Linux", "Caddy"],
|
|
},
|
|
{
|
|
label: "Practices",
|
|
skills: ["Agile / Scrum", "Relational Databases", "Self-hosting"],
|
|
},
|
|
];
|
|
|
|
const totalCount = skillGroups.reduce((n, g) => n + g.skills.length, 0);
|
|
|
|
export default function Skills() {
|
|
return (
|
|
<Widget title="tyler/skills" badge={totalCount} as="section">
|
|
<div className="flex flex-col">
|
|
{skillGroups.map(({ label, skills }) => (
|
|
<div
|
|
key={label}
|
|
className="flex flex-col xs:flex-row gap-1 xs:gap-6 py-3"
|
|
>
|
|
<span className="font-mono text-sm text-[var(--color-text-dim)] w-28 shrink-0">
|
|
{label}
|
|
</span>
|
|
<span className="font-mono text-sm text-[var(--color-text)]">
|
|
{skills.join(" · ")}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Widget>
|
|
);
|
|
}
|