feat: new website

This commit is contained in:
Dan Finch 2026-05-13 02:03:11 +02:00
commit 63a654aab4
64 changed files with 2904 additions and 0 deletions

1
src/ambient.d.ts vendored Normal file
View file

@ -0,0 +1 @@
import "@hypeup/lexicon"

1
src/routes/cv.html.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "ui/CV"

View file

@ -0,0 +1 @@
export { default } from "ui/Experience"

1
src/routes/index.html.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "ui/Home"

View file

@ -0,0 +1 @@
export { default } from "ui/Projects"

1
src/routes/tech.html.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "ui/Tech"

4
src/types.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare type Content = any
declare module "*?raw" { const src: string; export default src }
declare module "*?url" { const src: string; export default src }

192
src/ui/CV/CV.ts Normal file
View file

@ -0,0 +1,192 @@
import Reset from "ui/Layout/Reset"
import Markdown from "ui/shared/Markdown"
import intro from "ui/Home/intro.md?raw"
import details from "ui/Home/details.md?raw"
import skills from "ui/Home/skills.md?raw"
import experience from "ui/Experience/experience.md?raw"
import * as Tech from "ui/Tech/Sections"
import roboto from "@fontsource/roboto-condensed?url"
export default function CV() {
return [
doctype.html5,
html(
head(
link({ rel: "stylesheet", href: roboto }),
Theme(),
),
body(
main(
article(
Header(),
About(),
Skills(),
Experience(),
Expertise(),
),
),
a.pdf.noprint({ href: "cv.pdf" }, "PDF"),
),
),
]
}
function Header() {
return header(
h1("Dan Finch"),
div.links(
div.www(
a({ href: "https://da.nfin.ch" }, "Website"),
a(
{ href: "https://www.sigitex.com" },
"Open Source",
),
a(
{
href: "https://www.linkedin.com/in/danfinch",
},
"LinkedIn",
),
),
div.contact(
a(
{ href: "mailto:danfinch@outlook.com" },
"danfinch@outlook.com",
),
a({ href: "tel:+436645047583" }, "+43 664 5047583"),
),
),
)
}
function About() {
return section.about(
h2("About Me"),
Markdown(intro),
Markdown(details),
)
}
function Skills() {
return section.skills(h2("Skills"), Markdown(skills))
}
function Experience() {
return section.experience(
h2("Experience"),
Markdown(experience),
)
}
function Expertise() {
return section.expertise(
h2("Expertise"),
div.blocks(
Skillset("Core Languages", Tech.languages),
Skillset("Platforms", Tech.platforms),
Skillset("Frontend", Tech.frontend),
Skillset("Backend", Tech.backend),
Skillset("Data", Tech.data),
Skillset("Principles", Tech.principles),
Skillset("Architecture", Tech.architecture),
Skillset("Delivery", Tech.delivery),
Skillset("Quality", Tech.quality),
Skillset("Collaboration", Tech.collaboration),
),
)
}
function Skillset(name: string, content: string) {
return div.block(h3(name), Markdown(content))
}
const Color = {
backdrop: "#f0f0f0",
edge: "#808080",
} as const
function Theme() {
return style(
Reset(),
rule.pdf(
display.flex,
width("50px"),
height("50px"),
borderRadius("12px"),
backgroundColor("#345c42"),
color.white,
position.fixed,
top("25px"),
right("25px"),
justifyContent.center,
alignItems.center,
fontWeight.bold,
textDecoration.none,
),
$page(
prop("size", "auto"),
margin("0 0 0 0"),
padding("0.5in"),
),
rule(body, [
fontFamily("Roboto Condensed"),
fontSize("12pt"),
display.flex,
justifyContent.center,
backgroundColor(Color.backdrop),
]),
$media("print", [
rule(body, backgroundColor.white),
rule.noprint(raw("display: none !important;")),
]),
rule(main, [
backgroundColor.white,
width("8.5in"),
display.flex,
justifyContent.center,
paddingBottom("32pt"),
]),
rule(article, [width("7.0in")]),
rule(header, [
marginTop("0.5in"),
display.flex,
justifyContent.spaceBetween,
alignItems.flexEnd,
paddingBottom("8pt"),
]),
rule(h1, fontSize("28pt")),
rule.links(
fontSize("11pt"),
width("2.75in"),
display.flex,
justifyContent.spaceBetween,
alignItems.center,
rule(">div", [
display.flex,
flexDirection.column,
gap("2pt"),
]),
),
rule.contact(alignItems.flexEnd, fontSize("12pt")),
rule(section, [
borderTop(`double 3px ${Color.edge}`),
paddingTop("16pt"),
paddingBottom("16pt"),
]),
rule(h2, [marginBottom("12pt")]),
rule(p, [marginTop("12pt")]),
rule(ul, [marginTop("12pt")]),
rule(hr, [margin("12pt 0"), borderColor("#f0f0f0")]),
rule(".experience .content h2", [fontSize("14pt")]),
rule.expertise(
pageBreakInside.avoid,
rule(ul, marginBottom("12pt")),
rule.blocks(columns(3)),
rule.block(
flex(1),
minWidth("3in"),
breakInside.avoid,
),
),
)
}

1
src/ui/CV/index.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "./CV"

View file

@ -0,0 +1,26 @@
import Layout from "ui/Layout"
import Window from "ui/Layout/Window"
import Markdown from "ui/shared/Markdown"
import experience from "./experience.md?raw"
const title = "Dan Finch - Experience"
export default function Experience() {
return [
backgroundColor.white,
Layout(
{ title, url: "/experience" },
Window(
{ title },
div(
padding("16px"),
div.content(
h1("My Experience"),
hr(),
Markdown(experience),
),
),
),
),
]
}

View file

@ -0,0 +1,91 @@
## Dynatrace - Senior Software Engineer
> July 2023 - Present (Graz, Austria - Hybrid)
- Implemented an array of features and bug fixes for several Business Analytics applications using **React** and **TypeScript**.
- Designed and implemented a strongly-typed embedded DSL to generate the data warehouse queries which power the apps, significantly reducing maintenance overhead.
- Created an end-to-end testing framework suite based on **Playwright** that substantially streamlined writing and running tests.
- Created and integrated a state management framework based on **Zustand** and **Immer** which greatly simplified and optimized front-end code.
- Initiated ongoing effort to improve code quality and project structure.
- Migrated a large project from Bitbucket+Jenkins to Github+Actions.
---
## Independent Consultant
> Dec 2014 - July 2023 (Remote)
My freelance work in this time included:
- Created a mobile social network app for a startup conference and shared working space provider. This was written in functional **TypeScript** and **React Native**, with some Python/Django and Postgres on a Heroku backend.
- Consulted on JavaScript and front-end tech for Bitbanger Labs for their ColorSpike hybrid mobile app built on Cordova.
- Wrote a Chrome extension using the **Mithril** framework to inspect advertisements embedded in web pages for Bringhub.
- Designed and prototyped an app and backend for a IOT equipment monitoring startup in Sandpoint, ID using **React**, **ElasticSearch**, and **Postgres**.
- Created an **Electron** app connected to the QuickBooks API to build quotes for a greenhouse manufacturer.
---
## RiskLens - Senior Software Engineer
> Jun 2017 - Dec 2017 (Spokane, WA)
Helped lead the front-end development of FAIR-U - a **React** + **Redux**-based app backed by **C\#**, Sql Server, and
Azure & transitioned the team to TypeScript and other modern front-end tools. Gained experience with a
security-focused and process-intensive team.
---
## TenX Logic - Contract Developer
> Oct 2015 - Mar 2016 (Remote)
This project for the oil + gas industry targeted the Mono platform on ARM processors. I designed and
implemented a **C\#**-based driver model to control equipment in the field, with a frontend written in AngularJS.
---
## Kochava - Senior Developer
> Apr 2014 - Nov 2014 (Sandpoint, ID)
Worked on infrastructure and systems to manage large volumes of real-time analytics data. I tackled
complex challenges requiring constant inventiveness and collaboration to help the company scale at
a rapid rate.
My work centered around **Node.js**, **Redis**, Linux, and **MongoDB**. I also led the early implementation of a
scalable high-traffic analytics backend.
---
## Coldwater Creek - Web Developer
> Oct 2012 - Apr 2014 (Sandpoint, ID)
Worked with many teams to finish an amazing number of projects. My work on both internal tooling and
customer experience put the company ahead of its industry in many respects.
- Implemented a product search which outperformed all competitors, including a novel infinite scrolling method.
- Provided an array of features and user interfaces widgets for website with around one million hits per day.
- Wrote or re-wrote around 30 internal web-driven tools for e-commerce & marketing teams.
- Wrote custom jQuery UI powered framework and widgets for tool suite.
- Initiated effort to significantly improve client-side page load performance.
- Created a framework allowing mutual embedding between MVC and WebForms to allow a smooth transition off of legacy code.
---
## Promitheia - Technical Director
> Sep 2006 - Sep 2012 (Austin, TX)
As co-founder of this startup I was responsible for software design, programming, project management,
operations, testing, maintenance, and security.
- Created complex e-commerce website featuring retail and affiliate marketing management. Owner of dozens of projects within the company.
- Configured and managed production web servers including Microsoft IIS and LAMP stack.
- Worked closely with DBA on data-driven applications powered by SQL Server 2008 and ADO.NET.
- Analyzed and selected supporting software and services, including early adoption of technologies such as ASP.NET MVC, jQuery, F#, AWS, and distributed source control.
- Developed solutions using Service Oriented Architecture (SOA), WCF, and Windows services. Integrated software with various cloud data and web service providers, including AWS, Authorize.NET, and Zencoder.
- Studied and implemented e-commerce best practices, web security standards, and PCI compliance.
---
## Mercury ML - Founder
> 2002 - 2004 (Lapeer, MI)
I got my start right out of high school helping to scale two companies out of working with unwieldy spreadsheets.
- Released two major versions of a VB6 application to catalog marketing affiliates, track orders, and calculate payouts.
- Wrote and administered affiliate websites in Classic ASP and VBScript.

View file

@ -0,0 +1 @@
export { default } from "./Experience"

30
src/ui/Home/Home.ts Normal file
View file

@ -0,0 +1,30 @@
import Layout from "ui/Layout"
import Markdown from "ui/shared/Markdown"
import intro from "./intro.md?raw"
import details from "./details.md?raw"
import skills from "./skills.md?raw"
import interests from "./interests.md?raw"
import Window from "ui/Layout/Window"
const title = "Dan Finch - About Me"
export default function Home() {
return Layout(
{ title, url: "/" },
Window(
{ title },
div.content(padding("8px"), [
h1("About Me"),
Markdown(intro),
aside.fieldBorderDisabled(
img({ src: "/pfp.jpg", width: "200" }),
),
Markdown(details),
h2("Skills"),
Markdown(skills),
h2("Interests"),
Markdown(interests),
]),
),
)
}

5
src/ui/Home/details.md Normal file
View file

@ -0,0 +1,5 @@
My first principle is **user experience**. Software is interesting because of what it can do for people, and we all put up with mediocre software every day. This dissatisfaction drives me to set a high standard for our work.
As a programmer, I've learned to make code **small, simple, and readable**. My aim is prolific and expeditious code output while maintaining quality. I make loosely-coupled components, understandable abstractions, and low-ceremony frameworks.
As a teammate and leader, I am devoted to **friendliness** and **respect**. I am a good communicator, love sharing my knowledge, and learning from others. I've given talks at local user groups and mentored at work and in my free time.

1
src/ui/Home/index.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "./Home"

4
src/ui/Home/interests.md Normal file
View file

@ -0,0 +1,4 @@
- Compilers, interpreters & parsers.
- Metaprogramming, language-oriented programming.
- Developer tools - hacking on build tools and bundlers.
- Automating self-hosted cloud tech.

1
src/ui/Home/intro.md Normal file
View file

@ -0,0 +1 @@
I own software end-to-end: building or renewing stacks and evaluating the tools and techniques that will serve the product well. I assist my teams in creating frameworks, automation, standards, and guidance that clear the path to delivering good software and user experiences. I am comfortable working hands-on across code, architecture, UX, and product.

8
src/ui/Home/skills.md Normal file
View file

@ -0,0 +1,8 @@
- Consciousness of mainstream & emerging technology and trends.
- Quickly vetting and adapting to new technologies - building and improving stacks.
- High standards for great user and developer experiences.
- Interacting directly and empathetically with stakeholders and end users.
- Managing work through all phases of a development cycle.
- Eliminating friction in processes through automation.
- Understanding all the areas of a codebase and recognizing where waste can be reduced.
- Coordinating large, complex upgrades and refactors.

18
src/ui/Layout/Head.ts Normal file
View file

@ -0,0 +1,18 @@
const DEFAULT_TITLE = "Dan Finch"
export default function Head(props: { title: string }, ...contents: Content[]) {
return head(
meta({ charset: "utf-8" }),
meta({
name: "viewport",
content: "width=device-width, initial-scale=1.0",
}),
link({
rel: "shortcut icon",
type: "image/png",
href: "/favicon.png",
}),
title(props.title || DEFAULT_TITLE),
contents,
)
}

50
src/ui/Layout/Layout.ts Normal file
View file

@ -0,0 +1,50 @@
import Head from "./Head"
import background from "assets/background.js?raw"
import css98 from "98se.css?url"
import Taskbar from "ui/Layout/Taskbar"
import Theme from "ui/Layout/Theme"
export default function Layout(
props: { title: string, url: string },
...contents: Content[]
) {
return [
doctype.html5,
html(
Head(props, [
link({ rel: "stylesheet", href: css98 }),
style(Theme()),
]),
body(
backgroundColor.black,
canvas(
{ id: "background" },
display.block,
position.fixed,
width("100%"),
height("100%"),
left(0),
top(0),
zIndex(-1),
),
div(
display.flex,
flexDirection.column,
height("100dvh"),
nav(
height.maxContent,
Taskbar({ url: props.url }),
),
main(
flex(1),
overflowY.hidden,
display.flex,
justifyContent.center,
contents,
)
)
),
script(raw(background)),
),
]
}

14
src/ui/Layout/Reset.ts Normal file
View file

@ -0,0 +1,14 @@
export default function Reset() {
return [
rule("*, *::before, *::after", [boxSizing.borderBox]),
rule("*:not(dialog)", [margin(0)]),
rule(
"input, button, textarea, select",
font("inherit"),
),
rule(
"p, h1, h2, h3, h4, h5, h6",
overflowWrap.breakWord,
),
]
}

101
src/ui/Layout/Taskbar.ts Normal file
View file

@ -0,0 +1,101 @@
import favicon from "public/favicon.png?url"
import linkedin from "public/icons/linkedin.svg?raw"
import github from "public/icons/github.svg?raw"
import aboutIcon from "public/icons/about.png?url"
import experienceIcon from "public/icons/experience.png?url"
import projectsIcon from "public/icons/projects.png?url"
import techIcon from "public/icons/tech.png?url"
const vr = hr(height("20px"), margin("0px 2px"))
export default function Taskbar({ url }: { url: string }) {
return div.taskbar.window(
textWrapMode.nowrap,
overflowX.hidden,
margin("10px"),
div(
display.flex,
alignItems.center,
gap("2px"),
div(
display.flex,
paddingLeft("4px"),
img(
{ src: favicon },
width("12px"),
height("12px"),
marginRight("4px"),
),
div(fontWeight.bold, "Dan Finch"),
),
vr,
a(
{ title: "LinkedIn" },
{ href: "https://www.linkedin.com/in/danfinch/" },
raw(linkedin),
),
a(
{ title: "GitHub" },
{ href: "https://github.com/errilaz" },
raw(github),
),
vr,
Task({
icon: aboutIcon,
title: "About",
href: "/",
active: url === "/",
}),
Task({
icon: projectsIcon,
title: "Projects",
href: "/projects",
active: url === "/projects",
}),
Task({
icon: experienceIcon,
title: "Experience",
href: "/experience",
active: url === "/experience",
}),
Task({
icon: techIcon,
title: "Tech",
href: "/tech",
active: url === "/tech",
}),
),
)
}
function Task({
icon,
title,
href,
active,
}: {
icon: string
title: Content
href?: string
active?: boolean
}) {
return a.button(
{ href },
active && className("active"),
padding("0 4px"),
textAlign.left,
div(
width("100%"),
gap("4px"),
display.flex,
justifyContent.flexStart,
img(
{ src: icon },
width("12px"),
height("12px"),
marginRight("4px"),
),
span(title),
),
)
}

39
src/ui/Layout/Theme.ts Normal file
View file

@ -0,0 +1,39 @@
import Reset from "./Reset"
export default function Theme() {
return [
Reset(),
$media(
"(max-width: 468px)",
rule(
".taskbar .button",
minWidth("unset"),
rule("img", display.none),
),
),
rule("a.button", [
textDecoration.none,
cursor._default,
display.flex,
alignItems.center,
]),
rule(h1, fontSize("20px")),
rule(h2, fontSize("16px")),
rule(ul, paddingInlineStart("30px")),
rule.content(
textAlign.justify,
fontFamily("Arial"),
fontSize("14px"),
rule(h1, fontSize("28px")),
rule(h2, fontSize("20px")),
rule(h3, fontSize("14px"),paddingLeft("30px")),
rule(aside,
float.right,
marginLeft("20px"),
paddingBottom(0),
),
rule("p, h1, h2, h3, ul", margin("12px 0")),
),
rule(iframe, border(0)),
]
}

32
src/ui/Layout/Window.ts Normal file
View file

@ -0,0 +1,32 @@
const DEFAULT_WIDTH = "770px"
export default function Window(
props: { title: Content; width?: Content; flat?: boolean },
...contents: Content[]
) {
return article.window(
opacity(0.9),
marginTop("16px"),
marginBottom("24px"),
marginLeft("10px"),
marginRight("10px"),
width(props.width ?? DEFAULT_WIDTH),
maxWidth(props.width ?? DEFAULT_WIDTH),
display.flex,
flexDirection.column,
div.titleBar(
div.titleBarText(props.title),
div.titleBarControls(
button({ "aria-label": "Minimize" }),
button({ "aria-label": "Maximize" }),
button({ "aria-label": "Close" }),
),
),
div.windowBody(
props.flat ? null : className("field-border"),
flex(1),
overflowY.auto,
contents,
),
)
}

1
src/ui/Layout/index.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "./Layout"

View file

@ -0,0 +1,21 @@
import Layout from "ui/Layout"
import Window from "ui/Layout/Window"
const title = "Dan Finch - Projects"
const sigitex = "https://sigitex.com"
export default function Projects() {
return Layout(
{ title, url: "/projects" },
Window(
{
title: span(title, " (", a({ href: sigitex }, color.yellow, "SIGITEX.COM"), ")")
},
backgroundColor.black,
display.flex,
iframe({ src: "https://sigitex.com" },
flex(1)
),
),
)
}

1
src/ui/Projects/index.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "./Projects"

118
src/ui/Tech/Sections.ts Normal file
View file

@ -0,0 +1,118 @@
export const languages = `
- TypeScript
- JavaScript
- C#
- F#
- SQL
- HTML
- CSS
`
export const platforms = `
- Node.js
- Deno
- Bun
- Linux
- Electron
- Browser Extensions
- React Native
- .NET
`
export const frontend = `
- React
- Vite
- Zustand
- Immer
- Webpack
- Parcel
- Emotion
- Styled Components
- Mithril
- Redux
- React Router
- React Query
- Vanilla Extract
- CSS Modules
- SCSS, Stylus, Less
`
export const backend = `
- Express
- Koa
- ASP.NET
- Fastify
- Hapi
- NestJS
- Cloudflare
- Firebase
- Supabase
- AWS
`
export const data = `
- Postgres
- Redis
- Sqlite
- MongoDB
- MySQL
- SQL Server
`
export const principles = `
- DDD
- FP
- Explicit data flow
- Modular design
- Testable boundaries
- Composability
- Separation of concerns
- SOLID
- DRY
- KISS
- YAGNI
`
export const architecture = `
- Domain modeling
- API design
- REST
- Event-driven systems
- Service boundaries
- State management
- Caching
- Data modeling
- Progressive enhancement
`
export const delivery = `
- CI/CD
- Build and release automation
- Docker
- GitHub Actions
- IaC
- Feature flags
- Observability
`
export const quality = `
- TDD
- Unit testing
- Integration testing
- E2E testing
- Code review
- Refactoring
- Accessibility
- Responsive design
`
export const collaboration = `
- Async-first
- Agile
- Pairing
- Mentoring
- Technical leadership
- Product collaboration
- Design collaboration
- Documentation
`

48
src/ui/Tech/Tech.ts Normal file
View file

@ -0,0 +1,48 @@
import Layout from "ui/Layout"
import Window from "ui/Layout/Window"
import Markdown from "ui/shared/Markdown"
import * as Sections from "./Sections"
const title = "Dan Finch - Expertise"
export default function Expertise() {
return Layout(
{ title, url: "/tech" },
Window(
{ title, flat: true },
div(
[
display.flex,
flexWrap.wrap,
marginRight("8px"),
paddingBottom("16px"),
gap("8px"),
],
Skillset("Core Languages", Sections.languages),
Skillset("Platforms", Sections.platforms),
Skillset("Frontend", Sections.frontend),
Skillset("Backend", Sections.backend),
Skillset("Data", Sections.data),
Skillset("Principles", Sections.principles),
Skillset("Architecture", Sections.architecture),
Skillset("Delivery", Sections.delivery),
Skillset("Quality", Sections.quality),
Skillset("Collaboration", Sections.collaboration),
),
),
)
}
function Skillset(title: string, markdown: Content) {
return div(
[
flex(1),
minWidth("250px"),
display.flex,
flexDirection.column,
gap("4px"),
],
h2(title),
div.fieldBorder(flex(1), Markdown(markdown)),
)
}

1
src/ui/Tech/index.ts Normal file
View file

@ -0,0 +1 @@
export { default } from "./Tech"

View file

@ -0,0 +1,9 @@
import { Marked } from "marked"
import { gfmHeadingId } from "marked-gfm-heading-id"
export default function Markdown(md: string) {
return div.content(raw(marked.parse(md)))
}
const marked = new Marked({ gfm: true })
marked.use(gfmHeadingId())