View
Source
The website you are reading, explained by the website you are reading.
This is a project because the website is custom-coded, end to end. The website is the one you are reading. Nothing inside it came out of a template. I would not know how to use one.
The design system
Six colors. Two typefaces. One grid. A small, deliberately bounded vocabulary.
Color
Red is reserved for the active state. Hover, scroll progress, the italic word in a project title, the current page in the nav. Nothing else. Its rarity is the point.
Type
Self-hosted, variable. One file per family covers every weight and italic. The whole typography payload is about 200KB.
Grid
Mobile-first. The column is narrow by default and only earns more width when the viewport explicitly proves it can hold it.
Type scale
- 76 pxAadrop cap (Notes article opener)
- 48 pxView Sourceproject cover title
- 32 pxThe design systemsection heading
- 19 pxBody text, set in Garamond.article body · notes
- 17 pxItalic dek above the kicker.dek · tagline
- 13 pxcaption italicfigcaption · photo numerals
- 10 pxSECTION LABELnav · meta · subheadings
I am quietly proud of having built the whole thing myself. The advantage of building a website yourself is you get to show people exactly how it works. That is what the rest of this page is.
The stack
Built with Astro, a framework that turns the files I write into static HTML pages. Static pages load fast, do not require a server to be alive somewhere, and have the wonderful property of working when most other things on the internet do not.
There is no Tailwind. No styled-components. No CSS-in-JS. I wrote roughly 1,400 lines of plain CSS, the way people wrote CSS in 2008. Variables, media queries, the cascade. It is fewer lines of code than most React component libraries import before they render a button.
The fonts are EB Garamond for prose and Hanken Grotesk for the interface bits. Self-hosted, variable. The whole typography payload is about 200KB, which is the size of a single uncompressed phone photo.
The favicon is a red heart emoji. It is non-negotiable. I am not interested in feedback on the favicon.
- 9
- pages
- 23
- components
- 0
- tracking scripts
- 0
- cookie banners
- 0
- subscribe popups
- 1
- favicon (a heart)
Trackers, compared
- This site 0
- Average personal blog 14
- Average news website 35
- Heavier ad-driven sites 80
What's in the page
- Fonts 200 KB 52%
- Images 140 KB 37%
- CSS 22 KB 6%
- HTML 12 KB 3%
- JS 8 KB 2%
Page weight, compared
- This site 382 KB
- Average personal blog 2.5 MB
- Average news article 6.0 MB
- Average e-commerce home 4.5 MB
How this page got here
- Markdown src/content/
- Astro build npm run build
- Static HTML CSS · JS · images
- Netlify CDN edge nodes
- Your browser this tab
Other things that take about 90 seconds
- One TikTok 60 s
- This site, rebuilt from source 90 s
- Brushing teeth (dentist-approved) 120 s
- Microwave popcorn 180 s
The file tree
This is the source tree. Most production websites have more files in their cookie-consent dropdown than this site has in total.
src/├── assets/│ └── about/jacob-portrait.jpg├── components/│ ├── AboutCard.astro│ ├── AboutExpanded.astro│ ├── Article.astro│ ├── CardStack.astro│ ├── CodeBlock.astro│ ├── FileTreeBlock.astro│ ├── MiniDeck.astro│ ├── Nav.astro│ ├── ProjectCoverCard.astro│ ├── ProjectHeader.astro│ ├── StatsBlock.astro│ ├── TextBlock.astro│ └── TypewriterText.astro├── content/│ ├── about/index.md│ ├── notes/│ └── work/├── layouts/Base.astro├── pages/│ ├── index.astro│ ├── notes/│ └── work/├── scripts/stack.ts└── styles/ ├── components.css └── global.css
A small thing I am proud of
Here is the code that runs when you tap a project on the homepage. The card you tap zooms gently toward you. The other cards fly off in random directions. The page navigates after half a second.
It is eighteen lines of JavaScript. It took longer than the entire About page.
function playDispersal(selectedCard) {
const all = stackEl.querySelectorAll('.card');
all.forEach((c) => {
if (c === selectedCard) {
c.style.transform =
'translateY(0) scale(1.15) rotate(0deg)';
} else {
const sign = Math.random() < 0.5 ? -1 : 1;
const dx = sign * (380 + Math.random() * 260);
const dy = (Math.random() < 0.5 ? -1 : 1)
* (160 + Math.random() * 220);
const rot = sign * (14 + Math.random() * 22);
c.style.transform =
`translate(${dx}px, ${dy}px) rotate(${rot}deg)`;
c.style.opacity = '0';
}
});
} That little animation also broke, briefly, when users hit the back button on iOS Safari. Safari caches the page in the exact state you left it, which includes the inline transforms from the dispersal that was in progress when you navigated away. I discovered this approximately five minutes after a friend posted the site on Instagram.
I am not going to talk any further about iOS Safari.
The bug, the fix
- Tap project dispersal plays
- Project page user reads, scrolls
- bfcache restore deck stuck mid-flight
- pageshow handler reset, ready again
window.addEventListener('pageshow', (event) => {
if (!event.persisted) return;
isExiting = false;
const allCards = stackEl.querySelectorAll('.card');
allCards.forEach((c) => {
c.style.transition = 'none';
});
stack.update();
requestAnimationFrame(() => {
allCards.forEach((c) => {
c.style.transition = '';
});
});
}); Lines of code, compared
- This fix 12 lines
- A typical UI component 80 lines
- A typical CMS plugin 800 lines
- A typical app privacy policy 1,500 lines
The publishing workflow
When I push code to GitHub, Netlify notices, runs npm run build, and uploads the result to a content-delivery network. The whole process takes about 90 seconds. I have watched it happen approximately 400 times.
There is no content management system. The blog posts are markdown files in the same repository as the code. To publish a Note I write a file and push it. To edit one I edit the file. There is no admin panel and no draft state and no preview link. The trade-off is that I can only post when I am at a computer with the repository cloned. I have decided I am okay with this trade-off.
The shape of the site
Every page on this site is a real URL. They each emit their own title, meta description, Open Graph image, and canonical link. Nothing is rendered client-side. Nothing requires JavaScript to read. The sitemap below is the entire site, drawn out.
The sitemap
/ /work /notes /work/rampart-medical /work/lights-out /work/life-on-the-frontline /work/on-the-bike /work/turbo-s /work/view-source /notes/the-fifty-fifty-problem /notes/microdosing-creativity - jacobsarasohn.com
/ - Work
/work- Rampart Medical
/work/rampart-medical - Lights Out
/work/lights-out - Life on the Frontline
/work/life-on-the-frontline - On the Bike
/work/on-the-bike - Turbo S
/work/turbo-s - View Source
/work/view-source
- Rampart Medical
- Notes
/notes- The Fifty-Fifty Problem
/notes/the-fifty-fifty-problem - Microdosing Creativity
/notes/microdosing-creativity
- The Fifty-Fifty Problem
The honest part
I have now spent more time writing about this website than you will spend on it. This is a known issue.
The honest version of this page is that making a website by hand is one of the only ways left to fully control a small piece of the internet. The tools are free. The fonts are free. The hosting is roughly free. The whole thing fits inside one repository on one developer's laptop and yet is, for the moment, on every computer on earth that wants to look at it.
Right-click anywhere on this page and select View Source. That is the website. The whole thing is right there.