fixes and major changes

This commit is contained in:
2026-07-20 12:33:19 -05:00
parent 5d2c3dc545
commit 7369afb6bb
8 changed files with 304 additions and 77 deletions
+10 -10
View File
@@ -5,23 +5,23 @@ export type Area = {
export const practice: Area[] = [
{
title: 'Networking & Systems Administration',
body: 'Design and maintain business network infrastructure. Administer mail servers, web servers, and databases. Implement security and monitoring.',
title: 'Network and systems administration',
body: 'Design and maintain business networks, Linux servers, mail systems, web servers, and databases. Configure monitoring, backups, and access controls.',
},
{
title: 'Desktop Support & Technical Services',
body: 'End-user support across Windows, Mac, and Linux. Hardware and software troubleshooting, system imaging and deployment, IT asset management.',
title: 'IT support and endpoint management',
body: 'Support Windows, macOS, and Linux endpoints. Diagnose hardware and software problems, deploy system images, and maintain device inventories.',
},
{
title: 'Programming & Automation Engineering',
body: 'Automation through scripting and open-source development. Custom tools for operational workflows, CI/CD pipelines, and system monitoring.',
title: 'Software development and automation',
body: 'Write scripts and internal tools for operational workflows, CI/CD pipelines, infrastructure management, and system monitoring.',
},
{
title: 'Web Development & Frontend Engineering',
body: 'Web applications with Svelte and Tailwind. Responsive, accessible interfaces for internal tools and client work.',
title: 'Web application development',
body: 'Build responsive, accessible web applications and internal tools with TypeScript, Svelte, and Tailwind CSS.',
},
{
title: 'Creative Production & Media Services',
body: 'Video production, live audio-visual systems, and digital content for broadcast and clients.',
title: 'Media systems and production',
body: 'Operate live audiovisual systems and produce video and digital content for broadcast and client work.',
},
];
+48
View File
@@ -0,0 +1,48 @@
export type Project = {
name: string;
url: string;
type: string;
description: string;
};
export const projects: Project[] = [
{
name: "FOSSbench",
url: "https://fossbench.net",
type: "Platform",
description: "An open CPU benchmarking platform for submitting, searching, and comparing results across processors, operating systems, and compilers.",
},
{
name: "Wayviewer",
url: "https://github.com/owenrummage/wayviewer",
type: "Open source",
description: "A lightweight VNC viewer for Wayland, written in C++ with SDL2 and libvncclient. Includes command-line options and a graphical connection launcher.",
},
{
name: "ipinfo",
url: "https://github.com/owenrummage/ipinfo",
type: "Open source",
description: "A Go command-line application for looking up and displaying IP address information.",
},
{
name: "Wii-Linux",
url: "https://wii-linux.org",
type: "Infrastructure",
description: "Provide hosted compute and infrastructure sponsorship through NixLabs for the modern Wii-Linux project, with occasional contributions to project work.",
},
];
export const websites: Project[] = [
{
name: "NixLabs Networks",
url: "https://nixlabs.dev",
type: "Website",
description: "Designed and built the company website for NixLabs Networks, covering its managed IT, helpdesk, hosting, and infrastructure services.",
},
{
name: "Simple Logic IT Solutions",
url: "https://simplelogic.net",
type: "Website",
description: "Designed and built the service website for Simple Logic IT Solutions, including its managed IT, on-site support, service areas, and client portal access.",
},
];
+46 -14
View File
@@ -1,15 +1,47 @@
export const tools: string[] = [
'Linux',
'macOS',
'Windows',
'Python',
'TypeScript',
'Svelte',
'TailwindCSS',
'Docker',
'Git',
'GitHub',
'nginx',
'Azure',
'GoogleWorkspace',
export type Skill = {
name: string;
proficiency: "Advanced" | "Proficient" | "Working knowledge" | "Familiar";
};
export const tools: Skill[] = [
{ name: "Linux", proficiency: "Advanced" },
{ name: "Windows", proficiency: "Proficient" },
{ name: "macOS", proficiency: "Proficient" },
{ name: "Bash", proficiency: "Working knowledge" },
{ name: "Python", proficiency: "Proficient" },
{ name: "TypeScript", proficiency: "Proficient" },
{ name: "Svelte", proficiency: "Proficient" },
{ name: "Tailwind CSS", proficiency: "Working knowledge" },
{ name: "NGINX", proficiency: "Proficient" },
{ name: "Cisco", proficiency: "Proficient" },
{ name: "Ubiquiti", proficiency: "Proficient" },
{ name: "Juniper", proficiency: "Working knowledge" },
{ name: "Aruba", proficiency: "Familiar" },
{ name: "MikroTik", proficiency: "Familiar" },
{ name: "BGP", proficiency: "Working knowledge" },
{ name: "OSPF", proficiency: "Working knowledge" },
{ name: "VLANs", proficiency: "Proficient" },
{ name: "VPNs", proficiency: "Proficient" },
{ name: "DNS", proficiency: "Proficient" },
{ name: "DHCP", proficiency: "Proficient" },
{ name: "Proxmox", proficiency: "Proficient" },
{ name: "VMware", proficiency: "Working knowledge" },
{ name: "Hyper-V", proficiency: "Familiar" },
{ name: "KVM", proficiency: "Working knowledge" },
{ name: "Docker", proficiency: "Proficient" },
{ name: "Kubernetes", proficiency: "Working knowledge" },
{ name: "Cloudflare", proficiency: "Proficient" },
{ name: "Azure", proficiency: "Working knowledge" },
{ name: "AWS", proficiency: "Working knowledge" },
{ name: "Ansible", proficiency: "Working knowledge" },
{ name: "Terraform", proficiency: "Working knowledge" },
{ name: "GitHub Actions", proficiency: "Proficient" },
{ name: "Git", proficiency: "Working knowledge" },
{ name: "GitHub", proficiency: "Advanced" },
{ name: "Grafana", proficiency: "Working knowledge" },
{ name: "Prometheus", proficiency: "Working knowledge" },
{ name: "Zabbix", proficiency: "Working knowledge" },
{ name: "pfSense", proficiency: "Proficient" },
{ name: "OPNsense", proficiency: "Proficient" },
{ name: "Google Workspace", proficiency: "Proficient" },
];
+6 -6
View File
@@ -11,36 +11,36 @@ export const work: Role[] = [
title: "Owner & Operator",
company: "NixLabs Networks",
years: "2021 - 2024",
description: "Designed, deployed, and maintained secure network infrastructure, hosting platforms, and Linux-based systems for clients. Automated infrastructure management and operational workflows using Bash, Python, and open-source technologies. Provided technical support, troubleshooting, and systems administration for production server environments.",
description: "Designed and maintained client network infrastructure, hosting platforms, and Linux systems. Wrote Bash and Python automation for routine infrastructure work, and supported production servers through monitoring, troubleshooting, and system administration.",
},
{
title: "Technical Specialist",
company: "Computer House · Part-time",
years: "Jun - Aug 2023",
description: "Diagnosed and repaired customer computers, including hardware failures and software issues. Set up enterprise networks and servers for contract clients, while also handling storefront sales, customer service, and daily shop operations.",
description: "Diagnosed and repaired customer computers with hardware and software problems. Configured networks and servers for contract clients, handled storefront sales, and managed daily shop operations.",
},
{
title: "IT Support Intern",
company: "Metro Nashville Public Schools · Internship",
years: "Jan - Jul 2023",
description: "Deployed and configured Windows computers using Windows Deployment Services (WDS). Diagnosed hardware and software problems for staff and students across district facilities, tracked devices, and prepared equipment for technology rollouts.",
description: "Deployed Windows computers with Windows Deployment Services (WDS). Resolved hardware and software problems for staff and students, tracked district devices, and prepared equipment for technology rollouts.",
},
{
title: "System Architect",
company: "Fyra Labs · Part-time",
years: "Mar - Oct 2022",
description: "Designed and maintained the systems architecture behind the company's hosted services. Configured Cloudflare for DNS, traffic routing, and application security, and set up load balancing to keep services available during demand spikes.",
description: "Designed and maintained infrastructure for hosted services. Configured Cloudflare for DNS, traffic routing, and application security, then implemented load balancing to distribute traffic across services.",
},
{
title: "Technical Specialist",
company: "Computer House Inc · Seasonal",
years: "Jun - Aug 2022",
description: "Troubleshot and repaired desktops, laptops, and other computer hardware. Helped customers choose systems, replacement parts, and upgrades, prepared new computers for sale, and supported daily storefront operations.",
description: "Troubleshot and repaired desktops, laptops, and related hardware. Recommended systems, replacement parts, and upgrades to customers, prepared new computers for sale, and supported storefront operations.",
},
{
title: "Software Engineer",
company: "Innatical · Part-time",
years: "Apr - Oct 2021",
description: "Built and maintained back-end services in TypeScript, including APIs and server-side application features. Diagnosed bugs, reviewed changes, and tested releases with a remote development team.",
description: "Built and maintained TypeScript back-end services, APIs, and server-side application features. Diagnosed bugs, reviewed code changes, and tested releases with a remote development team.",
},
];
+76 -37
View File
@@ -3,9 +3,10 @@ import Layout from "../layouts/Layout.astro";
import { work } from "../data/work";
import { practice } from "../data/practice";
import { tools } from "../data/tools";
import { projects, websites } from "../data/projects";
const description =
"Systems engineer and founder building open, scalable infrastructure, cloud platforms, and networks that prioritize simplicity over complexity.";
"Systems engineer specializing in Linux infrastructure, network engineering, cloud platforms, and automation.";
---
<script>
@@ -22,9 +23,9 @@ const description =
</button>
</h1>
<p class="hero__statement">
Systems engineer and founder building open, scalable
infrastructure, cloud platforms, and networks that prioritize
simplicity over complexity.
Systems engineer specializing in Linux infrastructure, network
engineering, cloud platforms, and automation. Based in
Nashville, Tennessee.
</p>
<p class="hero__contact">
<a class="link" href="mailto:hello@rummage.cc"
@@ -47,7 +48,8 @@ const description =
<hr class="rule" />
<section class="work" aria-label="Work history">
<section class="work" aria-labelledby="experience-heading">
<h2 class="section-heading" id="experience-heading">Experience</h2>
{
work.map((role) => (
<article
@@ -62,19 +64,11 @@ const description =
aria-pressed="false"
data-role-pin
>
{role.current ? (
<span class="role__live">
<span>{role.years}</span>
</span>
) : (
<span class="role__years">
{role.years}
</span>
)}
<span class="role__years">{role.years}</span>
</button>
</div>
<div class="role__body">
<h2 class="role__title">{role.title}</h2>
<h3 class="role__title">{role.title}</h3>
<p class="role__company">{role.company}</p>
<p class="role__description">{role.description}</p>
</div>
@@ -85,16 +79,17 @@ const description =
<hr class="rule" />
<section class="education" aria-label="Education">
<section class="education" aria-labelledby="education-heading">
<h2 class="section-heading" id="education-heading">Education</h2>
<article class="role">
<div class="role__meta">
<span class="role__years">20232026</span>
<span class="role__years">2023 - 2026</span>
</div>
<div class="role__body">
<h2 class="role__title">
<h3 class="role__title">
Associate of Applied Science in Computer Information
Technology
</h2>
</h3>
<p class="role__company">
Nashville State Community College
</p>
@@ -108,17 +103,46 @@ const description =
<hr class="rule" />
<section class="practice" aria-label="Areas of practice">
<section class="projects" aria-labelledby="projects-heading">
<h2 class="section-heading" id="projects-heading">Projects</h2>
<div class="project-list">
{
[...projects, ...websites].map((project) => (
<article class="project">
<div class="project__meta">{project.type}</div>
<div class="project__body">
<h3 class="project__title">
<a
class="link link--ink"
href={project.url}
rel="noopener"
target="_blank"
>
{project.name}
</a>
</h3>
<p class="project__description">
{project.description}
</p>
</div>
</article>
))
}
</div>
</section>
<hr class="rule" />
<section class="practice" aria-labelledby="competencies-heading">
<h2 class="section-heading" id="competencies-heading">
Core competencies
</h2>
{
practice.map((area, i) => (
practice.map((area) => (
<article class="area">
<div class="area__meta">
{i === 0 && (
<span class="area__label">practice</span>
)}
</div>
<div class="area__meta" aria-hidden="true" />
<div class="area__body">
<h2 class="area__title">{area.title}</h2>
<h3 class="area__title">{area.title}</h3>
<p class="area__desc">{area.body}</p>
</div>
</article>
@@ -126,31 +150,46 @@ const description =
}
</section>
<section class="toolset" aria-label="Tools">
<div class="toolset__meta">
<span class="toolset__label">tools</span>
<section class="skills" aria-labelledby="skills-heading">
<h2 class="section-heading" id="skills-heading">
Technical skills
</h2>
<div class="toolset">
<div class="toolset__meta" aria-hidden="true"></div>
<ul class="toolset__list">
{
tools.map((skill) => (
<li
data-skill={skill.name}
data-proficiency={skill.proficiency}
tabindex="0"
aria-label={`${skill.name}: ${skill.proficiency}`}
>
<span>{skill.name}</span>
</li>
))
}
</ul>
</div>
<ul class="toolset__list">
{tools.map((t) => <li>{t}</li>)}
</ul>
</section>
<aside class="motto" aria-label="Closing note">
<div class="motto__meta" aria-hidden="true">motto</div>
<div class="motto__meta" aria-hidden="true">approach</div>
<button
class="motto__text"
type="button"
aria-label="Show another motto"
aria-label="Show another professional principle"
data-motto-play
>
I can explain BGP, but neither of us will enjoy it.
I build systems that can be understood and maintained by
someone other than me.
</button>
</aside>
<hr class="rule" />
<footer class="foot">
<span class="foot__build">build 0.1 / Nashville</span>
<span class="foot__build">Nashville, Tennessee</span>
<button
class="foot__home"
type="button"
+11 -7
View File
@@ -1,8 +1,8 @@
const mottos = [
"I can explain BGP, but neither of us will enjoy it.",
"It was DNS. Everyone take a drink.",
"The server is not haunted. I checked twice.",
"My favorite alert is the one that fixes itself before I get up.",
"I build systems that can be understood and maintained by someone other than me.",
"Reliable infrastructure starts with clear ownership and clear documentation.",
"Automation should reduce routine work without hiding how the system operates.",
"Fix the cause, document the change, and leave the system easier to support.",
];
const glyphs = "01#@$%&*+<>/\\";
@@ -84,8 +84,9 @@ function celebrate(): void {
}
function initSecrets(): void {
const home = document.querySelector<HTMLButtonElement>("[data-home-play]");
home?.addEventListener("click", celebrate);
document
.querySelector<HTMLButtonElement>("[data-home-play]")
?.addEventListener("click", celebrate);
const sequence = [
"ArrowUp",
@@ -101,7 +102,10 @@ function initSecrets(): void {
];
let position = 0;
window.addEventListener("keydown", (event) => {
position = event.key.toLowerCase() === sequence[position]?.toLowerCase() ? position + 1 : 0;
position =
event.key.toLowerCase() === sequence[position]?.toLowerCase()
? position + 1
: 0;
if (position === sequence.length) {
celebrate();
position = 0;
+1 -1
View File
@@ -14,7 +14,7 @@ const SCALE_ACTIVE = 1.06;
type ChipMap = Map<HTMLLIElement, DOMRect>;
function tokenOf(el: Element): string {
return (el.textContent || '').trim();
return (el as HTMLElement).dataset.skill || (el.textContent || '').trim();
}
function readReducedMotion(): boolean {
+106 -2
View File
@@ -320,6 +320,25 @@ button {
width: 100%;
}
/* ============================================================
* Section hierarchy
* ============================================================ */
.section-heading {
font-family: var(--font-sans);
font-size: clamp(1.375rem, 1.2rem + 0.6vw, 1.625rem);
font-weight: 500;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-tight);
color: var(--color-ink);
font-variation-settings: "wght" 540;
margin-bottom: clamp(0.75rem, 2vw, 1.25rem);
}
.section-heading + .role,
.section-heading + .area,
.section-heading + .toolset {
padding-top: clamp(0.75rem, 1.5vw, 1.25rem);
}
/* ============================================================
* Work index (marginalia grid)
* ============================================================ */
@@ -429,6 +448,50 @@ button {
color: var(--color-ink);
}
/* ============================================================
* Selected projects
* ============================================================ */
.project-list {
display: flex;
flex-direction: column;
}
.project {
display: grid;
grid-template-columns: var(--col-meta) 1fr;
gap: var(--gap-meta);
padding-block: clamp(1.5rem, 3vw, 2.25rem);
border-top: 1px solid transparent;
}
.project + .project {
border-top-color: var(--color-rule);
}
.project__meta {
padding-top: 0.35rem;
color: var(--color-muted);
font-family: var(--font-mono);
font-size: var(--fs-meta);
letter-spacing: var(--tracking-mono);
}
.project__body {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 64ch;
}
.project__title {
font-family: var(--font-sans);
font-size: var(--fs-role-title);
font-weight: 500;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-tight);
font-variation-settings: "wght" 520;
}
.project__description {
color: var(--color-ink-soft);
font-size: var(--fs-body);
line-height: var(--leading-body);
text-wrap: pretty;
}
/* ============================================================
* Practice (areas of competence — marginalia grid)
* ============================================================ */
@@ -490,6 +553,9 @@ button {
padding-block: clamp(1.75rem, 3.5vw, 2.5rem);
border-top: 1px solid var(--color-rule);
}
.skills {
margin-top: clamp(2.75rem, 5.5vw, 4rem);
}
.toolset__meta {
font-family: var(--font-mono);
font-size: var(--fs-meta);
@@ -547,6 +613,9 @@ button {
.toolset__list li.is-dragging::before {
color: var(--color-accent);
}
.toolset__list li.is-dragging::after {
opacity: 0;
}
/* Suppress hover styles on other chips while a drag is in flight, so
* they don't flash as the dragged chip sweeps over them. */
.toolset__list.is-dragging-list li:not(.is-dragging):hover {
@@ -572,15 +641,49 @@ body.is-toolset-dragging * {
color var(--dur-fast) var(--ease-out-quint),
transform var(--dur-base) var(--ease-out-quint);
}
.toolset__list li:hover {
.toolset__list li::after {
content: attr(data-proficiency);
position: absolute;
z-index: var(--z-focus);
left: 50%;
bottom: calc(100% + 0.55rem);
translate: -50% 0.25rem;
width: max-content;
max-width: 12rem;
padding: 0.4rem 0.55rem;
border: 1px solid var(--color-rule-strong);
border-radius: 3px;
background: var(--color-surface-rise);
color: var(--color-ink);
font-family: var(--font-sans);
font-size: var(--fs-tiny);
line-height: 1.25;
letter-spacing: 0;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition:
opacity var(--dur-fast) var(--ease-out-quint),
translate var(--dur-fast) var(--ease-out-quint),
visibility var(--dur-fast) var(--ease-out-quint);
}
.toolset__list li:hover,
.toolset__list li:focus-visible {
color: var(--color-accent);
background-color: var(--color-accent-wash);
transform: translateY(-1px);
}
.toolset__list li:hover::before {
.toolset__list li:hover::before,
.toolset__list li:focus-visible::before {
color: var(--color-accent);
transform: rotate(-14deg) translate(-1px, -1px);
}
.toolset__list li:hover::after,
.toolset__list li:focus-visible::after {
translate: -50% 0;
opacity: 1;
visibility: visible;
}
/* ============================================================
* Motto (closing reflection — marginalia grid)
@@ -730,6 +833,7 @@ body.is-toolset-dragging * {
@media (max-width: 640px) {
.role,
.area,
.project,
.toolset,
.motto {
grid-template-columns: 1fr;