Repository: alexcarpenter/alexcarpenter.me Branch: main Commit: 837e3b238481 Files: 228 Total size: 33.2 MB Directory structure: gitextract_15dbuwo8/ ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode/ │ └── settings.json ├── Makefile ├── README.md ├── astro.config.mjs ├── bin/ │ └── chezmoi ├── package.json ├── public/ │ ├── humans.txt │ └── robots.txt ├── src/ │ ├── components/ │ │ ├── demo.astro │ │ └── external-link.astro │ ├── consts.ts │ ├── content/ │ │ ├── gear/ │ │ │ ├── aer-day-sling-3.md │ │ │ ├── anker-laptop-charger.md │ │ │ ├── apple-studio-display.md │ │ │ ├── aqara-presence-sensor-fp2.md │ │ │ ├── aqara-smart-home-hub-m3.md │ │ │ ├── aqara-smart-lock-u100.md │ │ │ ├── aqara-water-leak-sensor.md │ │ │ ├── aqara-wireless-mini-switch.md │ │ │ ├── benchmade-mini-bugout.md │ │ │ ├── breville-barista-express-espresso-machine.md │ │ │ ├── caldigit-ts4-thunderbolt-4-dock.md │ │ │ ├── carepod-mini-ultrasonic-cool-mist-humidifier.md │ │ │ ├── cerave-hydrating-facial-cleanser.md │ │ │ ├── chris-reeve-small-sebenza-31.md │ │ │ ├── coway-airmega-air-purifier.md │ │ │ ├── coway-airmega-hepa-purifier.md │ │ │ ├── ecobee-smart-thermostat.md │ │ │ ├── eltamd-uv-daily-face-sunscreen.md │ │ │ ├── emr-tek-inferno-red-light.md │ │ │ ├── eufy-robot-lawn-mower-e15.md │ │ │ ├── eufy-security-camera-floodlight-camera-e340.md │ │ │ ├── eufy-security-homebase-s380.md │ │ │ ├── eufy-video-doorbell-e340.md │ │ │ ├── evergoods-civic-access-pouch-2l.md │ │ │ ├── evergoods-civic-half-zip.md │ │ │ ├── evergoods-civic-panel-loader-16l.md │ │ │ ├── fellow-atmos-vacuum-coffee-canister.md │ │ │ ├── focusrite-scarlett-audio-interface.md │ │ │ ├── foursevens-mini-turbo-mk-iii.md │ │ │ ├── global-6-inch-chef-s-knife.md │ │ │ ├── goruck-bullet-15l.md │ │ │ ├── hds-systems-edc-tactical.md │ │ │ ├── herman-miller-sayl-chair.md │ │ │ ├── hurom-h400-cold-press-juicer.md │ │ │ ├── ironmaster-super-bench-pro-v2.md │ │ │ ├── kalita-wave.md │ │ │ ├── kinesis-advantage-360-keyboard.md │ │ │ ├── knipex-cobra-water-pump-pliers.md │ │ │ ├── knipex-pliers-wrench.md │ │ │ ├── la-roche-posay-lipikar-ap-triple-repair-moisturizing-cream.md │ │ │ ├── lutron-aurora-smart-bulb-dimmer-switch.md │ │ │ ├── m4-mac-mini.md │ │ │ ├── maestri-house-rechargeable-milk-frother-with-stand.md │ │ │ ├── neutrogena-hydro-boost-water-cream.md │ │ │ ├── nitecore-nb-air.md │ │ │ ├── peak-design-packing-cube.md │ │ │ ├── pica-dry-longlife-automatic-pencil.md │ │ │ ├── powerblock-elite-exp-adjustable-dumbbells.md │ │ │ ├── prometheus-beta-qrv2-titanium.md │ │ │ ├── rocket-espresso-appartamento-tca-espresso-machine.md │ │ │ ├── rode-podmic-microphone.md │ │ │ ├── roka-oslo-2-0-wind-down.md │ │ │ ├── roost-laptop-stand.md │ │ │ ├── snow-peak-ti-double-h200-stacking-mug.md │ │ │ ├── sony-wh-1000xm4-headphones.md │ │ │ ├── spyderco-dragonfly-2.md │ │ │ ├── subminimal-flick-wdt-espresso-distribution-tool.md │ │ │ ├── synology-ds224.md │ │ │ ├── tactile-turn-apollo-flashlight.md │ │ │ ├── tactile-turn-rockwall-thumbstud.md │ │ │ ├── technivorm-moccamaster.md │ │ │ ├── the-james-brand-midland.md │ │ │ ├── timemore-sculptor-064s-coffee-grinder.md │ │ │ ├── tom-bihn-synik-22.md │ │ │ ├── tom-bihn-truck.md │ │ │ ├── toto-washlet-s5-electronic-bidet.md │ │ │ ├── tp-link-16-port-gigabit-poe-switch.md │ │ │ ├── tp-link-6e-mesh-system.md │ │ │ ├── unifi-dream-router-7.md │ │ │ ├── wera-bitholding-screwdriver.md │ │ │ ├── wera-tools-hex-set.md │ │ │ ├── yubikey-5c-nfc.md │ │ │ └── zebralight-sc5c-ii-le.md │ │ ├── jobs/ │ │ │ ├── clerk.md │ │ │ ├── hashicorp.md │ │ │ ├── masuga-design.md │ │ │ ├── mighty-in-the-midwest.md │ │ │ ├── nationbuilder.md │ │ │ └── watershed.md │ │ ├── notes/ │ │ │ ├── 2024-12-05-1.md │ │ │ ├── 2025-02-17-1.md │ │ │ ├── 2025-02-19-2.md │ │ │ ├── 2025-02-25-1.md │ │ │ ├── 2025-03-22-1.md │ │ │ ├── 2025-04-13-1.md │ │ │ ├── 2025-04-14-1.md │ │ │ ├── 2025-04-14-2.md │ │ │ ├── 2025-04-15-1.md │ │ │ ├── 2025-04-16-1.md │ │ │ ├── 2025-04-17-1.md │ │ │ ├── 2025-04-18-1.md │ │ │ ├── 2025-04-18-2.md │ │ │ ├── 2025-04-19-1.md │ │ │ ├── 2025-04-19-2.md │ │ │ ├── 2025-04-30-1.md │ │ │ ├── 2025-05-01-1.md │ │ │ ├── 2025-05-02-1.md │ │ │ ├── 2025-05-14-1.md │ │ │ ├── 2025-05-26-1.md │ │ │ ├── 2025-06-18-1.md │ │ │ ├── 2025-06-18-2.md │ │ │ ├── 2025-06-18-3.md │ │ │ ├── 2025-06-18-4.md │ │ │ ├── 2025-06-21-1.md │ │ │ ├── 2025-06-24-1.md │ │ │ ├── 2025-06-27-1.md │ │ │ ├── 2025-07-01-1.md │ │ │ ├── 2025-07-02-1.md │ │ │ ├── 2025-07-08-1.md │ │ │ ├── 2025-07-11-1.md │ │ │ ├── 2025-07-15-1.md │ │ │ ├── 2025-07-17-1.md │ │ │ ├── 2025-07-17-2.md │ │ │ ├── 2025-07-18-1.md │ │ │ ├── 2025-07-18-2.md │ │ │ ├── 2025-07-21-1.md │ │ │ ├── 2025-07-23-1.md │ │ │ ├── 2025-07-29-1.md │ │ │ ├── 2025-08-05-1.md │ │ │ ├── 2025-08-13-1.md │ │ │ ├── 2025-08-13-2.md │ │ │ ├── 2025-08-13-3.md │ │ │ ├── 2025-08-14-1.md │ │ │ ├── 2025-08-18-1.md │ │ │ ├── 2025-08-18-2.md │ │ │ ├── 2025-08-23-1.md │ │ │ ├── 2025-08-25-1.md │ │ │ ├── 2025-08-26-1.md │ │ │ ├── 2025-08-26-2.md │ │ │ ├── 2025-09-05-1.md │ │ │ ├── 2025-09-05-2.md │ │ │ ├── 2025-09-06-1.md │ │ │ ├── 2025-09-12-1.md │ │ │ ├── 2025-09-19-1.md │ │ │ ├── 2025-09-20-1.md │ │ │ ├── 2025-09-28-1.md │ │ │ ├── 2025-10-23-1.md │ │ │ ├── 2025-11-05-1.md │ │ │ ├── 2025-11-05-2.md │ │ │ ├── 2025-11-18-1.md │ │ │ ├── 2025-12-03-1.md │ │ │ ├── 2025-12-15-1.md │ │ │ ├── 2026-01-06-1.md │ │ │ ├── 2026-01-17-01.md │ │ │ ├── 2026-01-19-1.md │ │ │ ├── 2026-01-22-1.md │ │ │ ├── 2026-01-26-1.md │ │ │ ├── 2026-01-28-1.md │ │ │ ├── 2026-01-29-1.md │ │ │ ├── 2026-02-03-1.md │ │ │ ├── 2026-02-03-2.md │ │ │ ├── 2026-02-06-1.md │ │ │ ├── 2026-02-06-2.md │ │ │ ├── 2026-02-09-1.md │ │ │ ├── 2026-02-11-1.md │ │ │ ├── 2026-02-11-2.md │ │ │ ├── 2026-02-12-1.md │ │ │ ├── 2026-02-13-1.md │ │ │ ├── 2026-02-14-1.md │ │ │ ├── 2026-02-15-1.md │ │ │ ├── 2026-02-15-2.md │ │ │ ├── 2026-02-16-1.md │ │ │ ├── 2026-02-16-2.md │ │ │ ├── 2026-02-24-1.md │ │ │ ├── 2026-03-01-1.md │ │ │ ├── 2026-03-01-2.md │ │ │ ├── 2026-03-04-1.md │ │ │ ├── 2026-03-05-1.md │ │ │ ├── 2026-03-06-1.md │ │ │ ├── 2026-03-06-2.md │ │ │ ├── 2026-03-08-1.md │ │ │ ├── 2026-03-11-1.md │ │ │ ├── 2026-03-15-1.md │ │ │ ├── 2026-03-17-1.md │ │ │ ├── 2026-03-20-1.md │ │ │ ├── 2026-03-21-1.md │ │ │ ├── 2026-03-21-2.md │ │ │ ├── 2026-03-27-1.md │ │ │ ├── 2026-04-04-1.md │ │ │ └── 2026-04-26-1.md │ │ ├── oss-contributions.json │ │ └── recommendations/ │ │ ├── amy-stuart.md │ │ ├── andrew-possehl.md │ │ ├── benjamin-kohl.md │ │ ├── danielle-dunn.md │ │ ├── david-mosher.md │ │ ├── jimmy-merritello.md │ │ ├── kyle-luck.md │ │ └── melissa-taylor.md │ ├── content.config.ts │ ├── env.d.ts │ ├── layouts/ │ │ ├── base-layout.astro │ │ └── demo-layout.astro │ ├── lib/ │ │ ├── markdown.js │ │ └── utils.ts │ ├── pages/ │ │ ├── 404.astro │ │ ├── demos/ │ │ │ ├── bg-repeat-round.astro │ │ │ ├── box-decoration-break-clone.astro │ │ │ ├── button-disabled-color-mix.astro │ │ │ ├── button-tint.astro │ │ │ ├── hanging-punctuation.astro │ │ │ ├── inline-trailing-icon.astro │ │ │ ├── list-format-to-parts.astro │ │ │ ├── list-item-icon-baseline.astro │ │ │ └── modal-aspect-ratio.astro │ │ ├── index.astro │ │ ├── notes/ │ │ │ ├── [...page].astro │ │ │ ├── [...slug].astro │ │ │ └── rss.xml.js │ │ └── uses/ │ │ └── index.astro │ └── styles/ │ ├── demos.css │ └── global.css └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # build output dist/ # generated types .astro/ # dependencies node_modules/ # logs npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* # environment variables .env .env.production # macOS-specific files .DS_Store AGENT.md ================================================ FILE: .npmrc ================================================ min-release-age=7 ================================================ FILE: .prettierignore ================================================ node_modules/** package.json pnpm-lock.yaml ================================================ FILE: .prettierrc.json ================================================ { "plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"], "overrides": [ { "files": "*.astro", "options": { "parser": "astro" } } ] } ================================================ FILE: .vscode/settings.json ================================================ { "prettier.documentSelectors": ["**/*.astro"], "[astro]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } } ================================================ FILE: Makefile ================================================ checkpoint: @git add . @git commit -m "checkpoint at $$(date '+%Y-%m-%dT%H:%M:%S%z')" @git push @echo Checkpoint created and pushed to remote ================================================ FILE: README.md ================================================ # alexcarpenter.me - Framework: Astro - Deployment: Vercel - Styling: Tailwind CSS ## Running locally ```bash git clone https://github.com/alexcarpenter/alexcarpenter.me.git cd alexcarpenter.me pnpm install pnpm dev ``` ================================================ FILE: astro.config.mjs ================================================ import sitemap from "@astrojs/sitemap"; import { transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, } from "@shikijs/transformers"; import tailwindcss from "@tailwindcss/vite"; import icon from "astro-icon"; import { defineConfig } from "astro/config"; import rehypeExternalLinks from "rehype-external-links"; export default defineConfig({ site: "https://alexcarpenter.me", vite: { plugins: [tailwindcss()], define: { "import.meta.env.COMMIT_SHA": JSON.stringify( process.env.VERCEL_GIT_COMMIT_SHA || process.env.COMMIT_SHA || "56720e6", ), }, }, integrations: [sitemap(), icon()], markdown: { rehypePlugins: [[rehypeExternalLinks, { target: "_blank" }]], shikiConfig: { themes: { light: "github-light", dark: "github-dark", }, transformers: [ transformerNotationDiff(), transformerMetaHighlight(), transformerMetaWordHighlight(), ], cache: true, }, }, }); ================================================ FILE: bin/chezmoi ================================================ [File too large to display: 33.1 MB] ================================================ FILE: package.json ================================================ { "name": "alexcarpenter-me", "type": "module", "version": "7.0.0", "engines": { "node": ">=22" }, "pnpm": { "overrides": { "lodash": "4.17.23" } }, "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro check && astro build", "preview": "astro preview", "astro": "astro", "format": "prettier --write .", "check": "astro check" }, "dependencies": { "@astrojs/check": "0.9.9", "@astrojs/rss": "4.0.18", "@astrojs/sitemap": "3.7.2", "@shikijs/transformers": "^4.1.0", "@sindresorhus/slugify": "^3.0.0", "@tailwindcss/vite": "^4.3.0", "astro": "6.3.6", "astro-icon": "^1.1.5", "markdown-it": "^14.1.1", "rehype-external-links": "^3.0.0", "sanitize-html": "^2.17.4", "sharp": "^0.34.5", "tailwindcss": "4.2.4", "typescript": "^6.0.3" }, "devDependencies": { "@types/markdown-it": "^14.1.2", "@types/sanitize-html": "^2.16.1", "prettier": "^3.8.3", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-tailwindcss": "^0.7.4" }, "packageManager": "pnpm@10.33.0" } ================================================ FILE: public/humans.txt ================================================ TECHNOLOGIES - Astro: https://astro.build - Tailwind CSS: https://tailwindcss.com - Geist Font: https://vercel.com/font ================================================ FILE: public/robots.txt ================================================ Sitemap: https://alexcarpenter.me/sitemap-index.xml ================================================ FILE: src/components/demo.astro ================================================ --- import { Icon } from "astro-icon/components"; type Props = { src: string; title?: string; class?: string; aspectRatio?: "16:9" | "4:3" | "1:1"; }; const { src, title, class: className, aspectRatio = "16:9" } = Astro.props; ---
Open demo in a new tab
================================================ FILE: src/components/external-link.astro ================================================ --- import { Icon } from "astro-icon/components"; const { href, text, class: className } = Astro.props; --- {/* prettier-ignore */} {text} ================================================ FILE: src/consts.ts ================================================ export const GEAR_CATEGORIES = [ "bag", "coffee", "edc", "gym", "home", "wellness", "kitchen", "knife", "flashlight", "misc", "tools", "travel", "workspace", ] as const; type GearCategory = (typeof GEAR_CATEGORIES)[number]; export const GEAR_CATEGORY_MAP: Record< GearCategory, { title: string; description: string; slug: string; } > = { bag: { title: "Bags", description: "My current rotation of bags.", slug: "bags", }, coffee: { title: "Coffee", description: "My favorite coffee gear and accessories.", slug: "coffee", }, edc: { title: "Everyday Carry", description: "Tools I carry daily that fit into my pockets.", slug: "everyday-carry", }, flashlight: { title: "Flashlights", description: "My favorite flashlights and lighting gear.", slug: "flashlights", }, gym: { title: "Home Gym", description: "My home gym equipment and setup.", slug: "home-gym", }, wellness: { title: "Health & Wellness", description: "", slug: "health-and-wellness", }, home: { title: "Home", description: "My home automation setup.", slug: "home", }, kitchen: { title: "Kitchen", description: "My favorite kitchen gadgets and tools.", slug: "kitchen", }, knife: { title: "Knives", description: "Pocket knives and multi-tools.", slug: "knives", }, tools: { title: "Tools", description: "Homeowner tools and equipment.", slug: "tools", }, travel: { title: "Travel", description: "Gear I use while traveling and out and about.", slug: "travel", }, workspace: { title: "Workspace", description: "The tools I use for work.", slug: "workspace", }, misc: { title: "Miscellaneous", description: "Other gear that doesn't fit into a specific category.", slug: "miscellaneous", }, }; ================================================ FILE: src/content/gear/aer-day-sling-3.md ================================================ --- eyebrow: "Diaper bag" name: Aer Day Sling 3 category: bag link: https://aersf.com/collections/slings/products/day-sling-3 --- ================================================ FILE: src/content/gear/anker-laptop-charger.md ================================================ --- eyebrow: Laptop charger name: Anker Laptop Charger category: travel link: https://amzn.to/3JbHQdS --- ================================================ FILE: src/content/gear/apple-studio-display.md ================================================ --- eyebrow: Display name: Apple Studio Display category: workspace link: https://www.apple.com/studio-display/ --- ================================================ FILE: src/content/gear/aqara-presence-sensor-fp2.md ================================================ --- eyebrow: Presence Sensor name: Aqara Presence Sensor FP2 category: home link: https://amzn.to/4me46ma --- ================================================ FILE: src/content/gear/aqara-smart-home-hub-m3.md ================================================ --- eyebrow: Hub name: Aqara Smart Home Hub M3 category: home link: https://amzn.to/4nJzDNP --- ================================================ FILE: src/content/gear/aqara-smart-lock-u100.md ================================================ --- eyebrow: Lock name: Aqara Smart Lock U100 category: home link: https://amzn.to/3ImFIzM status: retired --- ================================================ FILE: src/content/gear/aqara-water-leak-sensor.md ================================================ --- eyebrow: Water sensor name: Aqara Water Leak Sensor category: home link: https://amzn.to/3IxiqHj --- ================================================ FILE: src/content/gear/aqara-wireless-mini-switch.md ================================================ --- eyebrow: Switch name: Aqara Wireless Mini Switch category: home link: https://amzn.to/4lSIE5r --- ================================================ FILE: src/content/gear/benchmade-mini-bugout.md ================================================ --- eyebrow: Lightweight name: Benchmade mini bugout category: knife link: https://benchmade.com/products/533bk-1-mini-bugout --- ================================================ FILE: src/content/gear/breville-barista-express-espresso-machine.md ================================================ --- status: retired eyebrow: Espresso machine name: Breville Barista Express Espresso Machine category: coffee link: https://amzn.to/4lpyCbt --- ================================================ FILE: src/content/gear/caldigit-ts4-thunderbolt-4-dock.md ================================================ --- eyebrow: Thunderbolt Doc name: CalDigit TS4 Thunderbolt 4 Dock category: workspace link: https://amzn.to/4p7vy73 --- ================================================ FILE: src/content/gear/carepod-mini-ultrasonic-cool-mist-humidifier.md ================================================ --- eyebrow: Humidifier name: Carepod Mini Ultrasonic Cool Mist Humidifier category: wellness link: https://amzn.to/4p7vy73 --- ================================================ FILE: src/content/gear/cerave-hydrating-facial-cleanser.md ================================================ --- eyebrow: Face Cleanser name: CeraVe Hydrating Facial Cleanser category: wellness link: https://amzn.to/4rf3wH9 --- ================================================ FILE: src/content/gear/chris-reeve-small-sebenza-31.md ================================================ --- eyebrow: Titanium name: Chris Reeve Small Sebenza 31 category: knife link: https://chrisreeve.com/collections/sebenza-31 favorite: true --- ================================================ FILE: src/content/gear/coway-airmega-air-purifier.md ================================================ --- eyebrow: Air purifier name: Coway Airmega Air Purifier category: home link: https://amzn.to/4lJGy7I --- ================================================ FILE: src/content/gear/coway-airmega-hepa-purifier.md ================================================ --- eyebrow: Air purifier name: Coway Airmega AP-1512HH(W) True HEPA Purifier category: wellness link: https://amzn.to/4gVxZWt --- ================================================ FILE: src/content/gear/ecobee-smart-thermostat.md ================================================ --- eyebrow: Thermostat name: ecobee Smart Thermostat category: home link: https://amzn.to/40hxmiV status: retired --- ================================================ FILE: src/content/gear/eltamd-uv-daily-face-sunscreen.md ================================================ --- eyebrow: Sunscreen name: EltaMD UV Daily Face Sunscreen category: wellness link: https://amzn.to/4aCSO6n --- ================================================ FILE: src/content/gear/emr-tek-inferno-red-light.md ================================================ --- eyebrow: Red light therapy name: EMR-TEK Inferno Red Light Panel category: wellness link: https://www.emr-tek.com/alexcarpenter --- ================================================ FILE: src/content/gear/eufy-robot-lawn-mower-e15.md ================================================ --- eyebrow: Lawnmower name: eufy Robot Lawn Mower E15 category: home link: https://eufy.com/products/t28801a1 --- ================================================ FILE: src/content/gear/eufy-security-camera-floodlight-camera-e340.md ================================================ --- eyebrow: Security Camera name: eufy Security Camera Floodlight Camera E340 category: home link: https://amzn.to/451OziV status: retired --- ================================================ FILE: src/content/gear/eufy-security-homebase-s380.md ================================================ --- eyebrow: Camera storage name: eufy Security HomeBase S380 category: home link: https://amzn.to/44Mcfqb --- ================================================ FILE: src/content/gear/eufy-video-doorbell-e340.md ================================================ --- eyebrow: Video doorbell name: eufy Video Doorbell E340 category: home link: https://eufy.com/products/t8214111 --- ================================================ FILE: src/content/gear/evergoods-civic-access-pouch-2l.md ================================================ --- eyebrow: Tech pouch name: EVERGOODS CIVIC Access Pouch 2L category: bag link: https://evergoods.us/collections/pouches/products/civic-access-pouch-2l --- ================================================ FILE: src/content/gear/evergoods-civic-half-zip.md ================================================ --- eyebrow: Everday bag name: EVERGOODS CIVIC Half Zip 22L category: bag link: https://evergoods.us/collections/backpacks/products/civic-half-zip --- ================================================ FILE: src/content/gear/evergoods-civic-panel-loader-16l.md ================================================ --- eyebrow: "Tech bag" name: EVERGOODS CIVIC Panel Loader 16L category: bag link: https://evergoods.us/collections/backpacks/products/civic-panel-loader?variant=42250173153414 --- ================================================ FILE: src/content/gear/fellow-atmos-vacuum-coffee-canister.md ================================================ --- eyebrow: "Coffee Storage" name: Fellow Atmos Vacuum Coffee Canister category: coffee link: https://amzn.to/4qIHQ5c --- ================================================ FILE: src/content/gear/focusrite-scarlett-audio-interface.md ================================================ --- eyebrow: Audio Interface name: Focusrite Scarlett Audio Interface category: workspace link: https://amzn.to/3HW1OsZ --- ================================================ FILE: src/content/gear/foursevens-mini-turbo-mk-iii.md ================================================ --- eyebrow: Mini name: Foursevens Mini Turbo Mk. III category: flashlight link: https://darksucks.com/products/mini-turbo-mkiii-ti-two-tone --- ================================================ FILE: src/content/gear/global-6-inch-chef-s-knife.md ================================================ --- eyebrow: Chefs knife name: Global 6 inch Chef's Knife category: kitchen link: https://amzn.to/46GLh5z favorite: true --- ================================================ FILE: src/content/gear/goruck-bullet-15l.md ================================================ --- eyebrow: Ruck name: Goruck Bullet 15L category: bag link: https://www.goruck.com/products/bullet-ruck-15l --- ================================================ FILE: src/content/gear/hds-systems-edc-tactical.md ================================================ --- eyebrow: Rotary UI name: HDS Systems EDC Tactical category: flashlight link: https://hdssystems.com/Products/Tactical/ favorite: true --- ================================================ FILE: src/content/gear/herman-miller-sayl-chair.md ================================================ --- eyebrow: Chair name: Herman Miller Sayl chair category: workspace link: https://amzn.to/4p6gl5X --- ================================================ FILE: src/content/gear/hurom-h400-cold-press-juicer.md ================================================ --- eyebrow: Juicer name: Hurom H400 Cold Press Juicer category: wellness link: https://amzn.to/4pyDeiQ --- ================================================ FILE: src/content/gear/ironmaster-super-bench-pro-v2.md ================================================ --- eyebrow: Adjustable Bench name: Ironmaster Super Bench PRO V2 category: gym link: https://www.ironmaster.com/products/super-bench-pro-v2/ --- ================================================ FILE: src/content/gear/kalita-wave.md ================================================ --- eyebrow: Pour over name: Kalita Wave category: coffee link: https://amzn.to/3JdOd0e --- ================================================ FILE: src/content/gear/kinesis-advantage-360-keyboard.md ================================================ --- eyebrow: Keyboard name: Kinesis Advantage 360 Keyboard category: workspace link: https://kinesis-ergo.com/keyboards/advantage360/ --- ================================================ FILE: src/content/gear/knipex-cobra-water-pump-pliers.md ================================================ --- eyebrow: Pliers name: KNIPEX Cobra Pliers Wrench category: tools link: https://amzn.to/4scQBq2 --- ================================================ FILE: src/content/gear/knipex-pliers-wrench.md ================================================ --- eyebrow: Pliers name: KNIPEX Pliers Wrench category: tools link: https://amzn.to/3MCR3xL --- ================================================ FILE: src/content/gear/la-roche-posay-lipikar-ap-triple-repair-moisturizing-cream.md ================================================ --- eyebrow: Body Moisturizer name: La Roche-Posay Lipikar AP+ Triple Repair Moisturizing Cream category: wellness link: https://amzn.to/4bVp6fc --- ================================================ FILE: src/content/gear/lutron-aurora-smart-bulb-dimmer-switch.md ================================================ --- eyebrow: Dimmer switch name: Lutron Aurora Smart Bulb Dimmer Switch category: home link: https://amzn.to/44wCl1N status: retired --- ================================================ FILE: src/content/gear/m4-mac-mini.md ================================================ --- eyebrow: Personal computer name: M4 Mac Mini category: workspace link: https://amzn.to/4d1AExL --- ================================================ FILE: src/content/gear/maestri-house-rechargeable-milk-frother-with-stand.md ================================================ --- eyebrow: Frother name: Maestri House Rechargeable Milk Frother with Stand category: kitchen link: https://amzn.to/4p1RnUi --- ================================================ FILE: src/content/gear/neutrogena-hydro-boost-water-cream.md ================================================ --- eyebrow: Face Moisturizer name: Neutrogena Hydro Boost Water Cream category: wellness link: https://amzn.to/4685v7O --- ================================================ FILE: src/content/gear/nitecore-nb-air.md ================================================ --- eyebrow: Power bank name: Nitecore NB Air category: travel link: https://amzn.to/4mf8YYf --- ================================================ FILE: src/content/gear/peak-design-packing-cube.md ================================================ --- eyebrow: Packing cube name: Peak Design Packing Cube category: travel link: https://amzn.to/4mdapq8 --- ================================================ FILE: src/content/gear/pica-dry-longlife-automatic-pencil.md ================================================ --- eyebrow: Pencil name: Pica-Dry Longlife Automatic Pencil category: tools link: https://amzn.to/4s37SSq --- ================================================ FILE: src/content/gear/powerblock-elite-exp-adjustable-dumbbells.md ================================================ --- eyebrow: Adjustable Dumbbells name: PowerBlock Elite EXP Adjustable Dumbbells category: gym link: https://amzn.to/4rti9Hr --- ================================================ FILE: src/content/gear/prometheus-beta-qrv2-titanium.md ================================================ --- eyebrow: Flashlight name: Prometheus Beta QRv2 Titanium category: edc link: https://darksucks.com/products/beta-qrv3-titanium-6al-4v --- ================================================ FILE: src/content/gear/rocket-espresso-appartamento-tca-espresso-machine.md ================================================ --- eyebrow: Espresso machine name: Rocket Espresso Appartamento TCA Espresso Machine category: coffee link: https://amzn.to/49Oc3uw --- ================================================ FILE: src/content/gear/rode-podmic-microphone.md ================================================ --- eyebrow: Microphone name: Rode PodMic Microphone category: workspace link: https://amzn.to/42h7wMM --- ================================================ FILE: src/content/gear/roka-oslo-2-0-wind-down.md ================================================ --- eyebrow: Red lens filter glasses name: ROKA Oslo 2.0 Wind Down category: wellness link: https://www.roka.com/products/oslo-2-0-huberman-blue-light-glasses --- ================================================ FILE: src/content/gear/roost-laptop-stand.md ================================================ --- eyebrow: Laptop stand name: Roost Laptop Stand category: travel link: https://amzn.to/4oDH49T --- ================================================ FILE: src/content/gear/snow-peak-ti-double-h200-stacking-mug.md ================================================ --- eyebrow: Mug name: snow peak Ti-Double H200 Stacking Mug category: coffee link: https://snowpeak.com/products/ti-double-h200-stacking-mug-tw-124 favorite: true --- ================================================ FILE: src/content/gear/sony-wh-1000xm4-headphones.md ================================================ --- eyebrow: Noise canceling name: Sony WH-1000XM4 Headphones category: travel link: https://amzn.to/4fIJDmY --- ================================================ FILE: src/content/gear/spyderco-dragonfly-2.md ================================================ --- eyebrow: Knife name: Spyderco Dragonfly 2 category: edc link: https://spyderco.com/products/dragonfly%E2%84%A2-2-lightweight --- ================================================ FILE: src/content/gear/subminimal-flick-wdt-espresso-distribution-tool.md ================================================ --- eyebrow: "Distribution Tool" name: Subminimal Flick WDT Espresso Distribution Tool category: coffee link: https://amzn.to/3OioxCm --- ================================================ FILE: src/content/gear/synology-ds224.md ================================================ --- eyebrow: Storage name: Synology DS224+ category: home link: https://amzn.to/3MV10GO --- ================================================ FILE: src/content/gear/tactile-turn-apollo-flashlight.md ================================================ --- eyebrow: Titanium name: Tactile Turn Apollo category: flashlight link: https://tactileturn.com/products/apollo?variant=41733727813737 --- ================================================ FILE: src/content/gear/tactile-turn-rockwall-thumbstud.md ================================================ --- eyebrow: Titanium name: Tactile Turn Rockwall Thumbstud category: knife link: https://tactileknife.co/products/rockwall-thumbstud --- ================================================ FILE: src/content/gear/technivorm-moccamaster.md ================================================ --- eyebrow: Coffee maker name: Technivorm Moccamaster category: coffee link: https://amzn.to/3HvPY8x favorite: true --- ================================================ FILE: src/content/gear/the-james-brand-midland.md ================================================ --- eyebrow: Key Management name: The James Brand Midland category: edc link: https://thejamesbrand.com/products/the-midland --- ================================================ FILE: src/content/gear/timemore-sculptor-064s-coffee-grinder.md ================================================ --- eyebrow: Espresso grinder name: TIMEMORE Sculptor 064S Flat Burr Grinder category: coffee link: https://amzn.to/46mbhTa --- ================================================ FILE: src/content/gear/tom-bihn-synik-22.md ================================================ --- eyebrow: "Family bag" name: TOM BIHN Synik 22 category: bag link: https://tombihn.com/collections/best-sellers/products/synik-22?variant=44784969253053 favorite: true --- ================================================ FILE: src/content/gear/tom-bihn-truck.md ================================================ --- eyebrow: "Grocery bag" name: TOM BIHN Truck category: bag link: https://tombihn.com/collections/tote-bags/products/the-truck?variant=42944967246013 favorite: true --- ================================================ FILE: src/content/gear/toto-washlet-s5-electronic-bidet.md ================================================ --- eyebrow: Bidet name: TOTO WASHLET S5 Electronic Bidet category: wellness link: https://amzn.to/4pvfagv --- ================================================ FILE: src/content/gear/tp-link-16-port-gigabit-poe-switch.md ================================================ --- eyebrow: Network switch name: TP-Link 16 Port Gigabit PoE Switch category: home link: https://amzn.to/44E00gN status: retired --- ================================================ FILE: src/content/gear/tp-link-6e-mesh-system.md ================================================ --- eyebrow: Mesh network name: TP-Link 6E Mesh System category: home link: https://amzn.to/4eQdCc1 status: retired --- ================================================ FILE: src/content/gear/unifi-dream-router-7.md ================================================ --- eyebrow: Router name: Unifi Dream Router 7 category: home link: https://techspecs.ui.com/unifi/cloud-gateways/udr7 --- ================================================ FILE: src/content/gear/wera-bitholding-screwdriver.md ================================================ --- eyebrow: Screwdriver name: Wera 7-In-1 Bitholding Screwdriver category: tools link: https://amzn.to/49uQRcD --- ================================================ FILE: src/content/gear/wera-tools-hex-set.md ================================================ --- eyebrow: Hex Set name: Wera Tools Hex Set category: tools link: https://amzn.to/4pHnQQG --- ================================================ FILE: src/content/gear/yubikey-5c-nfc.md ================================================ --- eyebrow: Security key name: YubiKey 5C NFC category: edc link: https://amzn.to/41zE4Bi --- ================================================ FILE: src/content/gear/zebralight-sc5c-ii-le.md ================================================ --- eyebrow: Classic name: Zebralight SC5c II LE category: flashlight link: https://zebralight.com/sc5c-mk-ii-le-aa-flashlight-neutral-white-high-cri-limited-edition_p_246.html --- ================================================ FILE: src/content/jobs/clerk.md ================================================ --- title: Staff UI Engineer company: Clerk startDate: 2024-01-04 projects: - title: Component theme editor link: https://clerk.com/components/theme-editor published: 2026-03-06 - title: Improve RTL support within components link: https://github.com/clerk/javascript/pull/7718 published: 2026-01-30 - title: Add UNSAFE_PortalProvider component link: https://github.com/clerk/javascript/pull/7310 published: 2026-01-21 - title: Add light-dark theme support link: https://github.com/clerk/javascript/pull/7560 published: 2026-01-20 - title: Interactive docs inline theme editing link: https://x.com/alexcarp_me/status/1970189970414399723 published: 2025-11-22 - title: shadcn/ui registry description: Get started with Clerk authentication in Next.js apps using the shadcn/ui CLI link: https://clerk.com/changelog/2025-08-13-shadcn-registry published: 2025-08-13 - title: New simple theme for easier customization description: A minimal theme with stripped-down styling that provides a clean foundation for custom designs. link: https://clerk.com/changelog/2025-07-29-theme-simple published: 2025-07-29 - title: shadcn/ui theme compatibility description: Introducing a new Clerk theme based on shadcn/ui that styles Clerk's components according to your shadcn/ui theme. link: https://clerk.com/changelog/2025-07-23-shadcn-theme published: 2025-07-23 - title: Clerk CSS variables support description: Clerk now supports theming via Clerk CSS variables. link: https://clerk.com/changelog/2025-07-15-clerk-css-variables-support published: 2025-07-15 - title: Clerk appearance object CSS variables support description: Clerk's appearance system now supports CSS variables for seamless design system integration and dynamic theming. link: https://clerk.com/changelog/2025-07-08-css-variables-support published: 2025-07-08 - title: CSS layer name support description: Introducing the cssLayerName option for compatibility with Tailwind CSS v4, allowing Clerk styles to be wrapped in a dedicated CSS cascade layer. link: https://clerk.com/changelog/2025-06-17-css-layer-name published: 2025-06-17 - title: Clerk Billing components link: https://clerk.com/docs/components/pricing-table published: - title: Combined sign-in-or-up description: Start collecting sign-in and sign-ups within a single flow. link: https://clerk.com/changelog/2025-01-16-sign-in-or-up published: 2025-01-16 - title: Clerk Elements description: Introducing Clerk Elements, a new set of unstyled UI primitives that make it easy to build completely custom user interfaces on top of Clerk's API. link: "https://clerk.com/changelog/2024-05-02-elements-beta" published: 2024-05-02 - title: Clerk.com user auth page link: "https://clerk.com/user-authentication" tools: - Next.js - Tailwind CSS - React Aria Components - TypeScript - Motion - Floating UI --- UI Engineer apart of the SDK team, working on UI Components and Dashboard UI. ================================================ FILE: src/content/jobs/hashicorp.md ================================================ --- title: Senior Lead Web Engineer company: HashiCorp startDate: 2021-07-01 endDate: 2023-07-01 tools: - Next.js - Dato CMS - Reach UI - TypeScript - CSS modules - Framer Motion --- Lead Web Engineer on the core web team, helped build and maintain public-facing HashiCorp websites and web applications with Next.js. ================================================ FILE: src/content/jobs/masuga-design.md ================================================ --- title: Front-end Developer company: Masuga Design startDate: 2012-02-01 endDate: 2015-08-01 tools: - ExpressionEngine - Craft CMS - SCSS - jQuery --- Front-end Developer building user interfaces for clients like A&E Networks, Image Comics, and Fox Networks Info with ExpressionEngine. ================================================ FILE: src/content/jobs/mighty-in-the-midwest.md ================================================ --- title: Senior Web Engineer company: Mighty in the Midwest startDate: 2015-08-01 endDate: 2018-11-01 projects: - title: Consrevation Legacy link: "https://conservationlegacy.org" - title: Dake Corp link: "https://dakecorp.com" - title: Brunswick Bowling link: "https://brunswickbowling.com" tools: - Craft CMS - Shopify - SCSS - jQuery --- Senior Developer building and maintaining client websites built with Craft CMS, ExpressionEngine, and Shopify. Lead front-end initiatives to improve CSS and JavaScript architecture and implement an atomic deployment pipeline for our projects. ================================================ FILE: src/content/jobs/nationbuilder.md ================================================ --- title: UI Engineer company: NationBuilder startDate: 2018-11-01 endDate: 2021-07-01 tools: - React - Rails - Bootstrap --- UI Engineer working on the design team, collaborating closely with engineers implementing new features and building out our design system Radius. ================================================ FILE: src/content/jobs/watershed.md ================================================ --- title: Lead Web Engineer company: Watershed startDate: 2023-08-01 endDate: 2023-12-01 projects: - title: Watershed homepage link: "https://watershed.com" - title: Watershed customers page link: "https://watershed.com/customers" - title: Watershed events page link: "https://watershed.com/events" - title: Watershed change campaign page link: "https://watershed.com/change" tools: - Next.js - Sanity CMS - CSS Modules - Framer Motion --- Web Engineer apart of the marketing department, built and maintained everything across watershed.com. ================================================ FILE: src/content/notes/2024-12-05-1.md ================================================ --- published: 2024-12-05T19:25:15-0400 title: Just use children link: https://www.sid.st/post/just-use-children/ tags: - bookmark --- ================================================ FILE: src/content/notes/2025-02-17-1.md ================================================ --- published: 2025-02-17T19:21:04-0400 title: The "everything bagel" of components link: https://dio.la/article/the-everything-bagel-of-components tags: - bookmark --- ================================================ FILE: src/content/notes/2025-02-19-2.md ================================================ --- published: 2025-02-19T16:37:34-0500 tags: - tip --- React Aria exposing state through the className is super handy. Here we're using the placement returned to define a Tailwind CSS variable which we can then use in Motion to define its animation direction. View sandbox example [here](https://codesandbox.io/p/sandbox/zwfndk). ```tsx {6-11,13} {open ? ( cx({ "[--y:4px]": placement === "top", "[--y:-4px]": placement === "bottom", }) } offset={6} initial={{ opacity: 0, y: "var(--y)" }} animate={{ opacity: 1, y: 0 }} > Content ) : null} ``` ================================================ FILE: src/content/notes/2025-02-25-1.md ================================================ --- published: 2025-02-25T19:26:08-0400 title: Sanding UI link: https://blog.jim-nielsen.com/2024/sanding-ui/ tags: - bookmark --- ================================================ FILE: src/content/notes/2025-03-22-1.md ================================================ --- published: 2025-03-22T19:25:15-0400 title: Naming things in design systems–and why it’s the worst link: https://pjonori.blog/posts/design-system-naming/ tags: - bookmark --- ================================================ FILE: src/content/notes/2025-04-13-1.md ================================================ --- published: 2025-04-13T20:14:22-0400 title: Write code that is easy to delete, not easy to extend link: https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to tags: - bookmark --- ================================================ FILE: src/content/notes/2025-04-14-1.md ================================================ --- published: 2025-04-14T12:41:36-0400 title: Animate height from 0 to auto tags: - tip - css - animation --- ```css .element { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.5s ease-in-out; } .element.open { grid-template-rows: 1fr; } ``` ## References: - [How to animate an element's height with CSS grid](https://www.stefanjudis.com/snippets/how-to-animate-height-with-css-grid/) - [Animating CSS Grid (How To + Examples) ](https://css-tricks.com/animating-css-grid-how-to-examples/) ================================================ FILE: src/content/notes/2025-04-14-2.md ================================================ --- published: 2025-04-14T20:50:41-0400 title: Create a list of links from an array using Intl.ListFormat with formatToParts demo: /demos/list-format-to-parts tags: - demo - javascript - tip --- ```tsx const tags = ["HTML", "CSS", "JavaScript"]; { new Intl.ListFormat("en-US").formatToParts(tags).map(({ type, value }) => { if (type === "element") { return {value}; } return value; }); } ``` which returns the following markup: ```html HTML, CSS, and JavaScript ``` ================================================ FILE: src/content/notes/2025-04-15-1.md ================================================ --- published: 2025-04-15T08:20:41-0400 tags: - css --- This was a fun layout challenge for some dashboard UI we've been working on at Clerk. The structure of the layout contains your typical sidebar and content elements that live side by side. Simplified markup example of what we are working with: ```html
``` The data element is a large table which i've hard coded to cause an overflow that needs to be able to scroll vertically and horizontally but we wanted to make sure that its scroll bars were always within the viewport. To accomplish this we can make use of `contain: size;` to ensure the content overflows within its container and not cause the page to need to scroll. ```css {29} body { min-height: 100%; display: flex; flex-direction: column; flex: 1; } .header { width: 100%; height: 56px; } .nav { width: 100%; height: 56px; position: sticky; } .layout { flex: 1; display: grid; grid-template-columns: 360px 1fr; } .sidebar {...} .content { overflow: auto; contain: size; } /* This is simply to cause an overflow to demonstrate the table size */ .data { width: 200vw; height: 200vh; } ``` View the demo CodePen [here](https://codepen.io/alexcarpenter/pen/emmOLoN?editors=0100). ================================================ FILE: src/content/notes/2025-04-16-1.md ================================================ --- published: 2025-04-16T08:45:17-0400 tags: - css - tip --- Stop vertically aligning your checkboxes with `center`. Instead use `baseline` to keep it aligned with the first line of the label text. ```scss label { display: flex; align-items: center; // [!code --] align-items: baseline; // [!code ++] } ``` ================================================ FILE: src/content/notes/2025-04-17-1.md ================================================ --- published: 2025-04-17T08:18:43-0400 tags: - css - tip --- Quick little improvement for elements that render dark even in light mode and have overflow, force the color-scheme to dark to blend the native form controls a bit better. ```css pre { color-scheme: dark; } ``` View demo on [Twitter](https://x.com/alexcarp_me/status/1823406174034649137/video/1). ================================================ FILE: src/content/notes/2025-04-18-1.md ================================================ --- published: 2025-04-18T07:59:33-0400 tags: - css - tip --- Quick little improvement for wrapping highlighted text. Make use of `box-decoration-break: clone;` to ensure elements fragments break across lines. ```css mark { box-decoration-break: clone; } ``` View demo on [Twitter](https://x.com/alexcarp_me/status/1821533842228089056/video/1). ================================================ FILE: src/content/notes/2025-04-18-2.md ================================================ --- published: 2025-04-18T08:02:53-0400 title: "OKLCH in CSS: why we moved from RGB and HSL" link: https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl tags: - css - bookmark --- ================================================ FILE: src/content/notes/2025-04-19-1.md ================================================ --- published: 2025-04-19T07:24:05-0400 title: The Wet Codebase link: https://www.deconstructconf.com/2019/dan-abramov-the-wet-codebase tags: - bookmark --- ================================================ FILE: src/content/notes/2025-04-19-2.md ================================================ --- published: 2025-04-19T07:29:11-0400 title: Adaptive dotted pattern link: https://codepen.io/myf/pen/poXYLNB?editors=0100 tags: - css --- ================================================ FILE: src/content/notes/2025-04-30-1.md ================================================ --- published: 2025-04-30T08:04:10-0400 tags: - tip --- Not uncommon to see folks add form submit handlers on the submit buttons click event vs on the forms submit handler. The problem is this prevents users from being able to fill out the form and submit it solely from the keyboard. ```jsx
``` ================================================ FILE: src/content/notes/2025-10-23-1.md ================================================ --- published: 2025-10-23T08:20:33-0400 title: Preserve modal aspect ratio across viewport sizes demo: /demos/modal-aspect-ratio tags: - demo - css --- This is one of those that feels like it should be easier than it ends up being, but here is where I landed at to ensure a modal image preserves its 3/2 aspect ratio no mater the viewport width/height. ```css .modal { aspect-ratio: 3 / 2; height: min(calc(100vh - 2rem), calc((100vw - 2rem) * 2 / 3)); margin: auto; position: relative; } ``` See it in action on [I Brew My Own Coffee](https://www.ibrewmyown.coffee). ================================================ FILE: src/content/notes/2025-11-05-1.md ================================================ --- title: "RESILIENT—UI interview #001 with Dima Belyaev" description: Staff front-end engineer based in Amsterdam, working on AI assistants at Shopify. link: https://www.resilient-ui.com/interviews/dima-belyaev published: 2025-11-05T07:52:09-0500 tags: - bookmark --- ================================================ FILE: src/content/notes/2025-11-05-2.md ================================================ --- title: The Web Animation Performance Tier List link: https://motion.dev/blog/web-animation-performance-tier-list published: 2025-11-05T10:20:46-0500 tags: - bookmark - animation --- ================================================ FILE: src/content/notes/2025-11-18-1.md ================================================ --- title: "RESILIENT—UI interview #002 with Hayden Bleasel" description: Australian Design Engineer who loves working on open source software for the web. link: https://www.resilient-ui.com/interviews/hayden-bleasel published: 2025-11-18T08:30:55-0500 tags: - bookmark --- ================================================ FILE: src/content/notes/2025-12-03-1.md ================================================ --- title: "2025 Holiday Coffee Gift Guide" description: Discover the best coffee gifts for the holidays—espresso machines, drippers, brewers, and bean subscriptions that coffee lovers will actually use every day. link: https://ibrewmyown.coffee/2025-holiday-gift-guide published: 2025-12-03T07:49:22-0500 tags: - bookmark --- ================================================ FILE: src/content/notes/2025-12-15-1.md ================================================ --- title: How to target Safari with a CSS @supports media query description: Easiest method for targeting Safari with CSS and Tailwind in 2025. link: https://wojtek.im/journal/targeting-safari-with-css-media-query published: 2025-12-15T08:03:15-0500 tags: - bookmark - css --- ================================================ FILE: src/content/notes/2026-01-06-1.md ================================================ --- published: 2026-01-06T17:02:58-0500 tags: - css - demo --- CSS only scroll fade example that I implemented on [ibrewmyown.coffee](https://github.com/alexcarpenter/ibrewmyown.coffee/commit/b5458aa1d151b14b085394c7fa9f2263320f1384): ```css @supports (animation-timeline: scroll()) { @property --fade-left { syntax: ""; inherits: false; initial-value: 0px; } @property --fade-right { syntax: ""; inherits: false; initial-value: 0px; } .scroll-fade { --fade-distance: 40px; mask-image: linear-gradient( to right, transparent 0, #000 var(--fade-left), #000 calc(100% - var(--fade-right)), transparent 100% ); mask-size: 100% 100%; mask-repeat: no-repeat; animation: fade-in-left 1 linear both, fade-out-right 1 linear both; animation-timeline: scroll(x self), scroll(x self); animation-range: 0% 12%, 88% 100%; } @keyframes fade-in-left { from { --fade-left: 0px; } to { --fade-left: var(--fade-distance); } } @keyframes fade-out-right { from { --fade-right: var(--fade-distance); } to { --fade-right: 0px; } } } ``` ================================================ FILE: src/content/notes/2026-01-17-01.md ================================================ --- title: 21 Lessons From 14 Years at Google link: https://addyosmani.com/blog/21-lessons/ published: 2026-01-17T08:17:52-0500 tags: - bookmark - work --- ================================================ FILE: src/content/notes/2026-01-19-1.md ================================================ --- title: Annotating for agents published: 2026-01-19T12:28:08-0500 link: https://benji.org/annotating tags: - bookmark - ai - privacy --- ================================================ FILE: src/content/notes/2026-01-22-1.md ================================================ --- published: 2026-01-22T16:17:27-0500 title: Greppability is an underrated code metric link: https://morizbuesing.com/blog/greppability-code-metric/ tags: - bookmark --- ================================================ FILE: src/content/notes/2026-01-26-1.md ================================================ --- published: 2026-01-26T14:34:05-0500 tags: - til --- TIL you can slow down animations in Chrome DevTools. Press cmd+shift+p (or ctrl+shift+p on Windows/Linux), then search for "animations". An Animations panel will appear at the bottom of DevTools where you can adjust the playback speed to 100%, 25%, or 10%. All animations on the page will now run at the selected speed. ================================================ FILE: src/content/notes/2026-01-28-1.md ================================================ --- published: 2026-01-28T06:47:36-0500 title: When life gives you lemons, write better error messages link: https://wix-ux.com/when-life-gives-you-lemons-write-better-error-messages-46c5223e1a2f tags: - bookmark --- 1. Say what happened 1. Say why it's a problem 1. Point out how to solve the problem ================================================ FILE: src/content/notes/2026-01-29-1.md ================================================ --- title: Composition is all you need. link: https://www.youtube.com/watch?v=4KvbVq3Eg5w published: 2026-01-29T07:21:29-0500 tags: - bookmark --- See also `vercel-composition-patterns` ```bash npx skills add vercel-labs/agent-skills ``` ================================================ FILE: src/content/notes/2026-02-03-1.md ================================================ --- published: 2026-02-03T07:06:47-0500 title: before-and-after link: https://www.jm.sv/before-and-after tags: - bookmark --- ================================================ FILE: src/content/notes/2026-02-03-2.md ================================================ --- published: 2026-02-03T08:43:34-0500 title: Alexander Vilinskyy's personal website link: https://www.vilinskyy.com/ tags: - bookmark - inspiration - website --- ================================================ FILE: src/content/notes/2026-02-06-1.md ================================================ --- title: A React trick to improve exit animations link: https://barvian.me/react-exit-animations published: 2026-02-06T07:50:26-0500 tags: - bookmark - javascript --- ================================================ FILE: src/content/notes/2026-02-06-2.md ================================================ --- title: Building Bulletproof React Components link: https://shud.in/thoughts/build-bulletproof-react-components published: 2026-02-06T07:50:48-0500 tags: - bookmark - javascript --- ================================================ FILE: src/content/notes/2026-02-09-1.md ================================================ --- title: RESILIENT-UI SKILLS link: https://www.resilient-ui.com/skills published: 2026-02-09T10:49:31-0500 tags: - javascript --- ```bash npx skills add alexcarpenter/resilient-ui ``` ================================================ FILE: src/content/notes/2026-02-11-1.md ================================================ --- title: Matt Rothenberg's personal website link: https://mattrothenberg.com/ published: 2026-02-11T07:43:56-0500 tags: - bookmark - inspiration - website --- ================================================ FILE: src/content/notes/2026-02-11-2.md ================================================ --- title: eslint-plugin-react-render-types link: https://github.com/HorusGoul/eslint-plugin-react-render-types published: 2026-02-11T07:47:53-0500 tags: - bookmark - javascript --- > ESLint plugin that brings Flow's Render Types to TypeScript via JSDoc. Enforce component composition constraints like `@renders {MenuItem}` at lint time. ================================================ FILE: src/content/notes/2026-02-12-1.md ================================================ --- title: Digital hygiene link: https://karpathy.bearblog.dev/digital-hygiene/ published: 2026-02-12T07:43:49-0500 tags: - bookmark - privacy --- ================================================ FILE: src/content/notes/2026-02-13-1.md ================================================ --- title: How I Use Claude Code link: https://boristane.com/blog/how-i-use-claude-code/ published: 2026-02-13T08:12:56-0500 tags: - ai --- ================================================ FILE: src/content/notes/2026-02-14-1.md ================================================ --- title: "ASCII characters are not pixels: a deep dive into ASCII rendering" link: https://alexharri.com/blog/ascii-rendering published: 2026-02-14T19:56:51-0500 tags: - bookmark --- ================================================ FILE: src/content/notes/2026-02-15-1.md ================================================ --- title: react-prehydrate link: https://github.com/javivelasco/react-prehydrate published: 2026-02-15T07:59:10-0500 tags: - bookmark --- > Eliminate flash-of-incorrect-state for user preferences in React Server Component apps. ================================================ FILE: src/content/notes/2026-02-15-2.md ================================================ --- title: "@nano_kit/store" link: https://nano_kit.js.org/store/ published: 2026-02-15T08:41:03-0500 tags: - bookmark --- > `@nano_kit/store` is a lightweight state management library inspired by Nano Stores and built around a push-pull based reactivity system. ================================================ FILE: src/content/notes/2026-02-16-1.md ================================================ --- title: Building Type-Safe Compound Components link: https://tkdodo.eu/blog/building-type-safe-compound-components published: 2026-02-16T08:21:11-0500 tags: - bookmark --- ================================================ FILE: src/content/notes/2026-02-16-2.md ================================================ --- title: link: https://torph.lochie.me/ published: 2026-02-16T11:48:22-0500 tags: - bookmark --- > Dependency-free animated text component. ================================================ FILE: src/content/notes/2026-02-24-1.md ================================================ --- title: ESLint complexity rule link: https://eslint.org/docs/latest/rules/complexity published: 2026-02-24T15:50:52-0500 --- ================================================ FILE: src/content/notes/2026-03-01-1.md ================================================ --- title: Anton Repponen's personal website link: https://repponen.com published: 2026-03-01T09:30:41-0500 tags: - inspiration - bookmark --- ================================================ FILE: src/content/notes/2026-03-01-2.md ================================================ --- title: Naz Hamid's personal website link: https://nazhamid.com/ published: 2026-03-01T11:25:34-0500 tags: - inspiration - bookmark --- ================================================ FILE: src/content/notes/2026-03-04-1.md ================================================ --- title: How to implement beautiful shadow borders link: https://x.com/PixelJanitor/status/1623358514440859649 published: 2026-03-04T11:25:34-0500 tags: - bookmark --- ================================================ FILE: src/content/notes/2026-03-05-1.md ================================================ --- title: History of Software Design link: https://historyofsoftware.org/ published: 2026-03-05T14:20:10-0500 --- ================================================ FILE: src/content/notes/2026-03-06-1.md ================================================ --- title: Smooth Dot Indicators with Embla Carousel and CSS color-mix() description: How I implemented smooth carousel pagination indicators that respond to scroll progress published: 2026-03-06T08:43:22-0500 --- The interview gallery on [ibrewmyown.coffee](https://ibrewmyown.coffee/interviews/fatih-arslan) uses image carousels with pagination dots. Standard pagination dots using Embla Carousel toggle between active and inactive, but I wanted them to respond to scroll progress: as you drag, the current indicator fades out while the next one fades in. ## Implementation [Embla Carousel](https://www.embla-carousel.com/) exposes scroll progress, making this possible: ```typescript const onScroll = (): void => { const snapList = emblaApi.scrollSnapList(); const progress = emblaApi.scrollProgress(); if (snapList.length < 2) return; let lower = snapList.length - 2; for (let i = 0; i < snapList.length - 1; i++) { if (progress >= snapList[i] && progress <= snapList[i + 1]) { lower = i; break; } } const upper = lower + 1; const range = snapList[upper] - snapList[lower]; const t = range === 0 ? 0 : (progress - snapList[lower]) / range; dotNodes.forEach((_, i) => { if (i === lower) setDotProgress(i, 1 - t); else if (i === upper) setDotProgress(i, t); else setDotProgress(i, 0); }); }; ``` As `t` interpolates from 0 to 1, a CSS custom property `--dot-progress` is set for each dot. This single value drives two animations: width expansion and color interpolation. ## Width and Color The dot width expands as you approach it: ```css width: calc(var(--spacing) * 2 + var(--spacing) * 3 * var(--dot-progress, 0)); ``` And the color blends between two states using `color-mix()`: ```css background-color: color-mix( in srgb, var(--color-neutral-600) calc(var(--dot-progress, 0) * 100%), var(--color-neutral-300) ); ``` This approach preserves contrast, keeping the active dot darkest and the inactive lightest. The browser handles both interpolations without additional JS overhead. Hat tip to [Derek Briggs](https://x.com/PixelJanitor/status/2029700875006922763) for the color fade idea: "What if the current active indicator faded out the primary color during the drag and the next active faded in so that the most active item had the most contrast always?" ================================================ FILE: src/content/notes/2026-03-06-2.md ================================================ --- title: React Grab link: https://www.react-grab.com/ published: 2026-03-06T19:23:27-0500 --- ================================================ FILE: src/content/notes/2026-03-08-1.md ================================================ --- title: Agentic Engineering Patterns link: https://simonwillison.net/guides/agentic-engineering-patterns/ published: 2026-03-08T09:19:33-0400 --- > Patterns for getting the best results out of coding agents like Claude Code and OpenAI Codex. ================================================ FILE: src/content/notes/2026-03-11-1.md ================================================ --- title: The Anatomy of an Agent Harness link: https://x.com/Vtrivedy10/status/2031408954517971368 published: 2026-03-11T07:18:23-0400 --- ================================================ FILE: src/content/notes/2026-03-15-1.md ================================================ --- title: Daniel Griesser's PI Config link: https://github.com/HazAT/pi-config published: 2026-03-15T19:53:02-0400 --- ================================================ FILE: src/content/notes/2026-03-17-1.md ================================================ --- title: "Managing Context Windows with pi /tree: Branches, Summaries, and Subagent-like Workflows" link: https://stacktoheap.com/blog/2026/02/26/pi-tree-context-window-management/ published: 2026-03-17T07:19:43-0400 --- ================================================ FILE: src/content/notes/2026-03-20-1.md ================================================ --- title: Using Container Queries in a CSS Grid to Detect Truncation link: https://v0-css-container-query-test.vercel.app/ published: 2026-03-20T08:37:14-0400 --- ================================================ FILE: src/content/notes/2026-03-21-1.md ================================================ --- title: William Jansson's personal website link: https://williamjansson.com/ published: 2026-03-21T08:29:03-0400 --- ================================================ FILE: src/content/notes/2026-03-21-2.md ================================================ --- title: Nguyễn Ngọc Ánh's personal website link: https://anh.ng/ published: 2026-03-21T08:31:11-0400 --- ================================================ FILE: src/content/notes/2026-03-27-1.md ================================================ --- title: Nikhil Anand's personal website link: https://nikhil.io/ published: 2026-03-27T14:49:41-0400 tags: - website --- ================================================ FILE: src/content/notes/2026-04-04-1.md ================================================ --- title: Tree structure using CSS anchor positioning link: https://lab.chsmc.org/anchor-positioned-tree published: 2026-04-04T14:35:11-0400 --- One of the coolest uses of CSS anchor positioning I have seen. ================================================ FILE: src/content/notes/2026-04-26-1.md ================================================ --- published: 2026-04-26T09:58:20-0400 demo: /demos/button-tint tags: - demo - css --- Tint buttons based on their foreground color for `:hover` and `:active` states. ```css button { --bg: transparent; --fg: currentColor; --am: 0%; background-color: color-mix(in srgb, var(--bg), var(--fg) var(--am)); color: var(--fg); &:hover { --am: 10%; } &:active { --am: 20%; } } .blue { --bg: oklch(48.8% 0.243 264.376); --fg: oklch(1 0 0); } .neutral { --bg: oklch(92.9% 0.013 255.508); --fg: oklch(27.8% 0.033 256.848); } ``` ================================================ FILE: src/content/oss-contributions.json ================================================ [ { "date": "2026-03-18", "link": "https://github.com/developit/kinu/pull/102", "description": "Allow clicking between radio/checkbox and labels", "status": "closed" }, { "date": "2026-02-17", "link": "https://github.com/anomalyco/opencode/pull/13987", "description": "Fix homepage video layout shift", "status": "merged" }, { "date": "2026-02-17", "link": "https://github.com/benjitaylor/agentation/pull/102", "description": "Fix keyboard interactions when collapsed, allow toggling via keyboard", "status": "closed" }, { "date": "2026-02-13", "link": "https://github.com/TanStack/tanstack.com/pull/712", "description": "Fix docs feature grid alignment and icon sizing", "status": "merged" }, { "date": "2026-02-05", "link": "https://github.com/cloudflare/kumo/pull/19", "description": "Wrap pagination counts with tabular-nums to avoid shifts", "status": "merged" }, { "date": "2026-01-29", "link": "https://github.com/vercel/next.js/pull/89245", "description": "Improve templates layout flexibility", "status": "merged" }, { "date": "2026-01-05", "link": "https://github.com/facebook/stylex/pull/1439", "description": "Fix confirm dialog closing when clicking inside.", "status": "merged" }, { "date": "2026-01-04", "link": "https://github.com/facebook/stylex/pull/1434", "description": "Fixes issues in docs codeblock component where the icons were not rendering properly before the title, causing the title to not be properly aligned with the code, but also just not rendering the icon visibly.", "status": "merged" }, { "date": "2026-01-04", "link": "https://github.com/facebook/stylex/pull/1433", "description": "Align docs callout component icon to the first line of text.", "status": "merged" }, { "date": "2025-09-30", "link": "https://github.com/fuma-nama/fumadocs/pull/2416", "description": "Add missing ring classes for buttonVariants to ensure focus states are visible for buttons.", "status": "merged" }, { "date": "2025-09-29", "link": "https://github.com/vercel/ai-chatbot/pull/1252", "description": "Ensure both PureModelSelectorCompact and VisibilitySelector have visible outlines when focus visible.", "status": "merged" }, { "date": "2025-09-29", "link": "https://github.com/haydenbleasel/kibo/pull/266", "description": "Fixes icon shrinking and alignment within pricing block when the feature text wraps to two lines.", "status": "merged" }, { "date": "2025-09-24", "link": "https://github.com/fuma-nama/fumadocs/pull/2399", "description": "Updates copy button to preserve the active state while in checked state.", "status": "merged" }, { "date": "2025-09-06", "link": "https://github.com/midday-ai/midday/pull/606", "description": "Ensure icons are aligned on the first line vs being vertically centered within the whole item.", "status": "merged" }, { "date": "2025-09-06", "link": "https://github.com/dodopayments/billingsdk/pull/144", "description": "Fix icon alignment within pricing-table-one", "status": "merged" }, { "date": "2025-07-04", "link": "https://github.com/shadcn-ui/ui/pull/7944", "description": "Add support for css imports", "status": "closed" }, { "date": "2023-09-03", "link": "https://github.com/rauchg/blog/pull/133", "description": "Defines color scheme when in light/dark modes.", "status": "merged" } ] ================================================ FILE: src/content/recommendations/amy-stuart.md ================================================ --- name: Amy Stuart title: Senior Designer company: nationbuilder published: 2021-01-02 --- Alex and I worked together on the design team at NationBuilder. Alex is a natural problem solver, and a talented visual designer. That combination makes him an excellent front-end engineer. We worked together on a new feature with a complex UI and he was able to interpret the designs with a care and detail that is rare, while also making thoughtful UX suggestions. Not to mention Alex is just a great person, easy to talk to and lovely to work with. ================================================ FILE: src/content/recommendations/andrew-possehl.md ================================================ --- name: Andrew Possehl title: Senior Designer company: nationbuilder published: 2021-01-01 --- I worked with Alex on a variety of different projects. He is excellent at his craft and a pleasure to collaborate with. His attention to detail always resulted in an extremely polished final product. ================================================ FILE: src/content/recommendations/benjamin-kohl.md ================================================ --- name: Benjamin Kohl title: Back-end Developer company: masuga-design published: 2015-01-01 status: hidden --- Alex is a true autodidact that is dedicated to keeping up with the latest development tools, methods and trends. ================================================ FILE: src/content/recommendations/danielle-dunn.md ================================================ --- name: Danielle Dunn title: Project Manager company: mighty-in-the-midwest published: 2018-01-02 --- Alex is incredibly thorough and thoughtful with his work and is always seeking out improving in his craft. He’s conscious of meeting deadlines and communicates concerns early so that project teams can be proactive in problem solving. ================================================ FILE: src/content/recommendations/david-mosher.md ================================================ --- name: David Mosher title: Staff Software Engineer company: clerk published: 2024-10-14 --- If you are seeking someone with deep expertise in web development, particularly with React, accessibility, animations, and a keen eye for design, then Alex is the perfect teammate. During my time at Clerk, I had the pleasure of working closely with Alex, and I can confidently say that he is an exceptional collaborator. His frontend web UI engineering skills are top-notch, and he is a fantastic human being. ================================================ FILE: src/content/recommendations/jimmy-merritello.md ================================================ --- name: Jimmy Merritello title: Web Engineer company: hashicorp published: 2023-01-01 --- Alex is an excellent teammate and an exceptionally talented web developer. When he joined the team we gained an incredibly strong collaborator. His thoughtful approach to both producing work and actively reviewing code instantly improved the entire teams workflow. It is clear that Alex is a life long learner and therefore is always sure to bring a new, novel approach to solve a problem. ================================================ FILE: src/content/recommendations/kyle-luck.md ================================================ --- name: Kyle Luck title: Developer company: mighty-in-the-midwest published: 2018-01-03 --- Alex is incredibly skilled, efficient, and thorough in his work. Perhaps more than any other co-worker I have had, Alex possesses a deep and wide understanding of modern web technologies, while his steady passion for producing best-of-class work inspires his peers to write cleaner, simpler, and more elegant code. ================================================ FILE: src/content/recommendations/melissa-taylor.md ================================================ --- name: Melissa Taylor title: Director Client Services company: mighty-in-the-midwest published: 2018-01-01 --- I would recommend Alex to any forward-thinking web team. His passion for web standards along with his friendly attitude made him an invaluable part of our development team. I`m always especially impressed with his commitment to continuous learning and I hope to have the chance to work with him again! ================================================ FILE: src/content.config.ts ================================================ import { defineCollection, reference } from "astro:content"; import { z } from "astro/zod"; import { file, glob } from "astro/loaders"; import { GEAR_CATEGORIES } from "./consts"; const gear = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/content/gear" }), schema: z.object({ name: z.string(), category: z.enum(GEAR_CATEGORIES), eyebrow: z.string().optional(), link: z.url().optional(), favorite: z.boolean().optional().default(false), status: z.enum(["active", "retired"]).default("active"), }), }); const notes = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/content/notes" }), schema: z.object({ published: z.coerce.date(), title: z.string().optional(), description: z.string().optional(), link: z.string().optional(), tags: z.array(z.string()).optional(), demo: z.string().optional(), }), }); const jobs = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/content/jobs" }), schema: z.object({ title: z.string(), company: z.string(), startDate: z.coerce.date(), endDate: z.coerce.date().optional(), tools: z.array(z.string()).optional(), projects: z .array( z.object({ title: z.string(), description: z.string().optional(), link: z.string(), published: z.coerce.date().optional(), }), ) .optional(), }), }); const recommendations = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/content/recommendations" }), schema: z.object({ name: z.string(), title: z.string(), company: reference("jobs"), published: z.coerce.date(), avatar: z.string().optional(), status: z.enum(["visible", "hidden"]).default("visible"), }), }); const ossContributions = defineCollection({ loader: file("src/content/oss-contributions.json", { parser: (fileContent) => { const data = JSON.parse(fileContent); return data.map((item: Record, index: number) => ({ id: `contribution-${index + 1}`, ...item, })); }, }), schema: z.object({ link: z.url(), date: z.coerce.date(), description: z.string(), status: z.enum(["open", "merged", "closed"]).default("open"), }), }); export const collections = { gear, jobs, notes, ossContributions, recommendations, }; ================================================ FILE: src/env.d.ts ================================================ /// /// ================================================ FILE: src/layouts/base-layout.astro ================================================ --- import "@/styles/global.css"; const canonicalURL = new URL(Astro.url.pathname, Astro.site); const { title, description } = Astro.props; const metaTitle = title ? `${title} // Alex Carpenter` : "Alex Carpenter"; const metaDescription = description ? description : "Staff UI Engineer at Clerk"; import { version } from "../../package.json"; import ExternalLink from "@/components/external-link.astro"; const COMMIT_SHA = import.meta.env.COMMIT_SHA.slice(0, 7); --- {metaTitle} Skip to content

Alex Carpenter

Staff UI Engineer at Clerk

Grand Rapids, MI.

Connect

{ [ { title: "GitHub", username: "@alexcarpenter", link: "https://github.com/alexcarpenter", }, { title: "Twitter", username: "@alexcarp_me", link: "https://twitter.com/alexcarp_me", }, { title: "Bluesky", username: "@alexcarpenter.me", link: "https://bsky.app/profile/alexcarpenter.me", }, { title: "LinkedIn", username: "@alexcarpenter", link: "https://www.linkedin.com/in/imalexcarpenter/", }, ].map((social) => { return (
{social.title}
); }) }
Version
v{version}
Last Modified
{ new Date().toLocaleDateString("en-US", { year: "numeric", month: "short", day: "2-digit", }) }
Commit
================================================ FILE: src/layouts/demo-layout.astro ================================================ --- import "@/styles/demos.css"; --- ================================================ FILE: src/lib/markdown.js ================================================ import MarkdownIt from "markdown-it"; let markdownParser = null; export function getMarkdownParser() { if (!markdownParser) { markdownParser = new MarkdownIt({ html: true, linkify: true, typographer: true, }); } return markdownParser; } ================================================ FILE: src/lib/utils.ts ================================================ export function enforceExhaustive( value: never, message = "Unexpected value", ): never { throw new Error(`${message} '${value}'`); } export function formatLinkHostname(urlString: string): string { const url = new URL(urlString); // For Twitter/X links, show x.com/username or twitter.com/username if (url.hostname.includes("twitter.com") || url.hostname.includes("x.com")) { const username = url.pathname.split("/")[1]; return username ? `${url.hostname}/${username}` : url.hostname; } // For all other links, just show the hostname return url.hostname; } ================================================ FILE: src/pages/404.astro ================================================ --- import BaseLayout from "@/layouts/base-layout.astro"; ---

404 Page not found.

================================================ FILE: src/pages/demos/bg-repeat-round.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; ---
================================================ FILE: src/pages/demos/box-decoration-break-clone.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; ---

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti, repellendus aspernatur quisquam inventore aliquam maxime laboriosam maiores provident illum.

================================================ FILE: src/pages/demos/button-disabled-color-mix.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; ---
================================================ FILE: src/pages/demos/button-tint.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; ---
================================================ FILE: src/pages/demos/hanging-punctuation.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; ---

Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste officia quasi fugiat, dolores ab nam repellendus voluptate.

================================================ FILE: src/pages/demos/inline-trailing-icon.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; import { Icon } from "astro-icon/components"; ---

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, ducimus

================================================ FILE: src/pages/demos/list-format-to-parts.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; const tags = ["HTML", "CSS", "JavaScript"]; ---

{ new Intl.ListFormat("en-US") .formatToParts(tags) .map(({ type, value }) => { if (type === "element") { return ( // prettier-ignore {value} ); } return value; }) }

================================================ FILE: src/pages/demos/list-item-icon-baseline.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; import { Icon } from "astro-icon/components"; ---
    { [ "List item 1", "List item 2 that is longer than the others and wraps to two lines", "List item 3", ].map((i) => (
  • {" "} {i}
  • )) }
================================================ FILE: src/pages/demos/modal-aspect-ratio.astro ================================================ --- import DemoLayout from "@/layouts/demo-layout.astro"; ---
================================================ FILE: src/pages/index.astro ================================================ --- import slugify from "@sindresorhus/slugify"; import { getCollection, render } from "astro:content"; import { Icon } from "astro-icon/components"; import BaseLayout from "@/layouts/base-layout.astro"; import { getEntry } from "astro:content"; import { enforceExhaustive } from "@/lib/utils"; import ExternalLink from "@/components/external-link.astro"; const jobs = await getCollection("jobs"); const recommendations = await getCollection("recommendations"); const ossContributions = await getCollection("ossContributions"); const renderedJobs = await Promise.all( jobs.map(async (job) => { const { Content } = await render(job); return { job, Content }; }), ); const renderedRecommendations = await Promise.all( recommendations.map(async (recommendation) => { const { Content } = await render(recommendation); const company = await getEntry(recommendation.data.company); return { recommendation, company, Content }; }), ); ---

Steward of the web building composable interfaces for humans and agents.

Currently on the SDK team at Clerk, I work on UI components and theming infrastructure-building systems for customization, improving component APIs, and creating developer tooling that makes authentication interfaces more flexible and accessible.

I do my best work on small, highly collaborative teams that ship often and embrace the convergence of human craftsmanship with AI tooling. I care deeply about design and believe the best interfaces emerge when designers, engineers, and intelligent systems work together.

View notes

Projects

    { [ { logomark: ( ), name: "I Brew My Own Coffee", link: "https://ibrewmyown.coffee", description: // prettier-ignore <> A curated collection of home coffee setups. Built with{" "} {new Intl.ListFormat("en-US", { style: "long", type: "conjunction", }).format(["Astro", "Tailwind CSS", "Base UI"])}. , }, { logomark: ( ), name: "Curated", link: "https://curated.alexcarpenter.me", description: // prettier-ignore <> Weekly curated items including design inspiration, tools, and resources. Built with{" "} {new Intl.ListFormat("en-US", { style: "long", type: "conjunction", }).format(["Astro"])}. , }, { logomark: ( ), name: "RESILIENT—UI", link: "https://resilient-ui.com", description: // prettier-ignore <> Resources for building resilient user interfaces. Built with{" "} {new Intl.ListFormat("en-US", { style: "long", type: "conjunction", }).format([ "Next.js", "Tailwind CSS", "Base UI", "shadcn/ui", "Fumadocs", ])}. , }, { logomark: ( ), name: "dotfiles", link: "https://curated.alexcarpenter.me", description: // prettier-ignore <> My home config, scripts and installation process. Managed with{" "} {new Intl.ListFormat("en-US", { style: "long", type: "conjunction", }).format(["Stow"])}. , }, ] .filter((p) => p.name !== "dotfiles") .map((project) => (
  • {project.logomark}

    {project.description}

  • )) }

Experience

    { renderedJobs .sort( (a, b) => Date.parse(b.job.data.startDate.toString()) - Date.parse(a.job.data.startDate.toString()), ) .map(({ job, Content }, index) => (
  • {job.data.company}

    {" "} {job.data.title}

    – {job.data.endDate ? ( ) : ( )}

    {job.data.tools ? (

    {new Intl.ListFormat("en-US", { style: "long", type: "conjunction", }).format(job.data.tools)}

    ) : null}
  • )) }

Recommendations

    { renderedRecommendations .sort( (a, b) => Date.parse(b.recommendation.data.published.toString()) - Date.parse(a.recommendation.data.published.toString()), ) .filter( ({ recommendation }) => recommendation.data.status === "visible", ) .map(({ recommendation, company, Content }) => (
  • {recommendation.data.name}

    {" "} {recommendation.data.title}, {company.data.company}

  • )) }

OSS Contributions

    { ossContributions .sort( (a, b) => Date.parse(b.data.date.toString()) - Date.parse(a.data.date.toString()), ) .map((contribution) => { const match = contribution.data.link.match( /github\.com\/([^\/]+\/[^\/]+)/, ); const repo = match ? match[1] : null; if (!repo) return; return (
  • { switch (contribution.data.status) { case "open": return "git-pull-request-arrow"; case "merged": return "git-merge"; case "closed": return "git-pull-request-closed"; default: enforceExhaustive( contribution.data.status, "Unknown contribution status", ); } })()} />

    {contribution.data.description}

  • ); }) }
================================================ FILE: src/pages/notes/[...page].astro ================================================ --- import type { GetStaticPaths, Page } from "astro"; import { getCollection, render } from "astro:content"; import type { CollectionEntry } from "astro:content"; import { Icon } from "astro-icon/components"; import BaseLayout from "@/layouts/base-layout.astro"; import { formatLinkHostname } from "@/lib/utils"; import ExternalLink from "@/components/external-link.astro"; import Demo from "@/components/demo.astro"; type Props = { page: Page>; }; export const getStaticPaths: GetStaticPaths = async ({ paginate }) => { const notes = await getCollection("notes"); const sortedNotes = notes.sort( (a, b) => Date.parse(b.data.published.toString()) - Date.parse(a.data.published.toString()), ); return paginate(sortedNotes, { pageSize: 20 }); }; const { page } = Astro.props as Props; const renderedNotes = await Promise.all( page.data.map(async (note) => { const { Content } = await render(note); return { note, Content }; }), ); const heading = page.currentPage === 1 ? "Latest" : `Page ${page.currentPage}`; ---

Notes on engineering, developer experience, design systems, and agentic engineering.

{heading}

{ (page.url.prev || page.url.next) && ( ) }
================================================ FILE: src/pages/notes/[...slug].astro ================================================ --- import { getCollection, render } from "astro:content"; // import slugify from "@sindresorhus/slugify"; import BaseLayout from "@/layouts/base-layout.astro"; import ExternalLink from "@/components/external-link.astro"; import { formatLinkHostname } from "@/lib/utils"; import Demo from "@/components/demo.astro"; export async function getStaticPaths() { const notes = await getCollection("notes"); return notes.map((n) => ({ params: { slug: n.id }, props: n, })); } const note = Astro.props; const { Content } = await render(note); ---
{ note.data.title ? (

{note.data.link ? ( ) : ( note.data.title )}

{note.data.link ? (

{formatLinkHostname(note.data.link)}

) : null}
) : null } { note.data.demo ? ( ) : null } { note.body ? (
) : null }
================================================ FILE: src/pages/notes/rss.xml.js ================================================ import rss from "@astrojs/rss"; import { getCollection } from "astro:content"; import sanitizeHtml from "sanitize-html"; import { getMarkdownParser } from "@/lib/markdown.js"; const parser = getMarkdownParser(); export async function GET(context) { const notes = await getCollection("notes"); return rss({ title: "Notes - Alex Carpenter", description: "Notes on engineering, developer experience, design systems, and accessibility.", site: context.site, items: notes .sort((a, b) => b.data.published - a.data.published) .map((post) => ({ title: post.data.title, description: post.data.description, pubDate: post.data.published, link: `/notes/${post.id}`, content: post.body && sanitizeHtml(parser.render(post.body), { allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]), }), })), }); } ================================================ FILE: src/pages/uses/index.astro ================================================ --- import { getCollection } from "astro:content"; import BaseLayout from "@/layouts/base-layout.astro"; import { GEAR_CATEGORY_MAP, GEAR_CATEGORIES } from "@/consts"; import ExternalLink from "@/components/external-link.astro"; import slugify from "@sindresorhus/slugify"; const gear = await getCollection("gear"); const activeGear = gear.filter((item) => item.data.status === "active"); const groupedGear = Array.from( activeGear.reduce((map, item) => { const key = item.data.category; if (!map.has(key)) { map.set(key, []); } map.get(key)!.push(item); return map; }, new Map()), ).sort(([categoryA], [categoryB]) => { return ( GEAR_CATEGORIES.indexOf(categoryA as (typeof GEAR_CATEGORIES)[number]) - GEAR_CATEGORIES.indexOf(categoryB as (typeof GEAR_CATEGORIES)[number]) ); }); ---

Uses

{ groupedGear.map(([category, items]) => { const categoryInfo = GEAR_CATEGORY_MAP[category as keyof typeof GEAR_CATEGORY_MAP]; if (!categoryInfo || items.length === 0) return null; return (

{categoryInfo.title}

    {items.map((item) => (
  • {item.data.eyebrow && (

    {item.data.eyebrow}

    )}

    {item.data.link ? ( ) : ( item.data.name )}

    {item.data.link && (

    {new URL(item.data.link).hostname}

    )}
  • ))}
); }) }
================================================ FILE: src/styles/demos.css ================================================ @import "tailwindcss"; @variant dark (@media (prefers-color-scheme: dark)); @theme { --color-background: var(--background); --color-foreground: var(--foreground); --color-muted: var(--muted); --color-muted-foreground: --alpha(var(--color-foreground) / 65%); --color-accent: var(--accent); } :root { --background: var(--color-white); --foreground: var(--color-black); --muted: var(--color-neutral-200); --accent: var(--color-blue-600); } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; --background: var(--color-black); --foreground: var(--color-white); --muted: var(--color-neutral-800); --accent: var(--color-blue-400); } } @layer utilities { .bg-grid { background: conic-gradient( from 90deg at 50% 50%, transparent 0deg 90deg, var(--color-muted) 90deg 180deg, transparent 180deg 270deg, var(--color-muted) 270deg 360deg ); background-size: 8px 8px; } } ================================================ FILE: src/styles/global.css ================================================ @import "tailwindcss"; @font-face { font-family: "GeistMono"; src: url("/fonts/GeistMono[wght].woff2") format("woff2"); font-weight: 400 500; font-style: normal; font-display: swap; } @variant dark (@media (prefers-color-scheme: dark)); @theme { --font-*: initial; --font-mono: "GeistMono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --color-background: var(--background); --color-foreground: var(--foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-separator: var(--separator); --color-red: var(--red); --color-blue: var(--blue); --color-green: var(--green); --color-orange: var(--orange); --color-purple: var(--purple); } :root { --background: var(--color-zinc-50); --foreground: var(--color-zinc-800); --muted: var(--color-zinc-200); --muted-foreground: var(--color-zinc-500); --separator: var(--color-zinc-200); --red: var(--color-red-600); --blue: var(--color-blue-600); --green: var(--color-green-600); --orange: var(--color-orange-600); --purple: var(--color-purple-600); } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; --background: var(--color-zinc-950); --foreground: var(--color-zinc-50); --muted: var(--color-zinc-800); --muted-foreground: var(--color-zinc-400); --separator: var(--color-zinc-800); --red: var(--color-red-500); --blue: var(--color-blue-500); --green: var(--color-green-500); --orange: var(--color-orange-500); --purple: var(--color-purple-500); } .astro-code, .astro-code span { color: var(--shiki-dark) !important; } } @layer base { html { height: 100%; background-color: var(--color-background); } body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; display: flex; flex-direction: column; min-height: 100%; background-color: var(--color-background); color: var(--color-foreground); } mark, :not(pre) > code { background-color: var(--color-muted); color: inherit; padding-inline: var(--spacing); box-decoration-break: clone; border-radius: var(--radius-sm); } pre { --pre-background-color: light-dark( var(--color-zinc-100), var(--color-zinc-900) ); padding-block: calc(var(--spacing) * 4); font-size: var(--text-xs); line-height: 1.5; background-color: var(--pre-background-color) !important; border-radius: var(--radius-md); border: 1px solid var(--color-separator); scrollbar-width: thin; scrollbar-color: var(--color-separator) transparent; outline-offset: 4px; } pre code { isolation: isolate; display: block; width: fit-content; min-width: 100%; counter-reset: line; & .line { --line-background-color: var(--pre-background-color); --line-border-color: var(--line-background-color); display: inline-block; width: 100%; counter-increment: line; background-color: var(--line-background-color); @media (hover: hover) { &:hover { --line-background-color: light-dark( var(--color-zinc-200), var(--color-zinc-800) ); } } &::before { pointer-events: none; position: sticky; left: 0; z-index: 20; display: inline-block; width: 2.75rem; background: linear-gradient( to right, var(--line-background-color) 80%, transparent ); padding-right: 1rem; text-align: right; color: var(--color-muted-foreground); content: counter(line); user-select: none; border-left: 2px solid var(--line-border-color); } &::after { pointer-events: none; position: sticky; right: 0; z-index: 10; display: inline-block; width: 1.5rem; background: linear-gradient( to left, var(--line-background-color) 35%, transparent ); content: " "; user-select: none; } &.highlighted, &.remove, &.add { --line-background-color: color-mix( in oklab, var(--line-border-color), var(--pre-background-color) 90% ); @media (hover: hover) { &:hover { --line-background-color: color-mix( in oklab, var(--line-border-color), var(--pre-background-color) 80% ); } } &::before { color: var(--line-border-color); } } &.highlighted { --line-border-color: light-dark( oklch(0.4949 0.180091 257.6013), oklch(0.7685 0.1214 252.34) ); } &.remove { --line-border-color: light-dark( oklch(0.5879 0.1927 20.47), oklch(0.7214 0.1616 15.49) ); &::before { content: "-"; } } &.add { --line-border-color: light-dark( oklch(0.5464 0.1442 147.32), oklch(0.8512 0.1406 150.34) ); &::before { content: "+"; } } } } pre[data-language="bash"] { .line::before { content: "$"; } } } @layer components { .prose { color: var(--color-muted-foreground); & > :where(* + *) { margin-top: calc(var(--spacing) * 5); } & :where(a) { color: var(--color-foreground); text-decoration: underline; text-decoration-color: var(--color-muted-foreground); } & :where(blockquote) { display: flex; column-gap: var(--spacing); &::before { content: "\BB"; } } & :where(:is(h1, h2, h3)) { color: var(--color-foreground); } & :where(ul) { display: flex; flex-direction: column; row-gap: var(--spacing); } & :where(li) { position: relative; padding-inline-start: calc(1ch + var(--spacing)); &::before { content: "\203A"; position: absolute; top: 0; left: 0; } } } } @layer utilities { .bg-grid { background: conic-gradient( from 90deg at 50% 50%, transparent 0deg 90deg, var(--color-muted) 90deg 180deg, transparent 180deg 270deg, var(--color-muted) 270deg 360deg ); background-size: 8px 8px; } } ================================================ FILE: tsconfig.json ================================================ { "extends": "astro/tsconfigs/base", "include": [".astro/types.d.ts", "**/*"], "exclude": ["dist"], "compilerOptions": { "baseUrl": "./src", "paths": { "@/*": ["*"] }, "moduleResolution": "bundler" } }