Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified assets/img/header.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/assets/img/header.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/__tests__/pages/feed.xml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ describe('feed.xml', () => {
// Content is framed: a "new post" lead-in, the rendered body, and a book CTA.
expect(item.content).toContain('<p>Rendered HTML</p>');
expect(item.content).toContain('A new post from');
expect(item.content).toContain(siteConfig.bookUrl);
expect(item.content).toContain(siteConfig.bookUrlEmail);
expect(item.content.indexOf('A new post from')).toBeLessThan(
item.content.indexOf('<p>Rendered HTML</p>'),
);
expect(item.content.indexOf('<p>Rendered HTML</p>')).toBeLessThan(
item.content.indexOf(siteConfig.bookUrl),
item.content.indexOf(siteConfig.bookUrlEmail),
);
expect(item.link).toBe('https://ben.balter.com/2024/06/15/my-post/');
expect(item.author).toBe(siteConfig.email);
Expand Down
26 changes: 14 additions & 12 deletions src/components/BookCta.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
/**
* Book CTA Component
*
* Promotes the Open & Async book with a link to the book site.
* Promotes the Open & Async book with a buy link to the book site.
* Supports two variants: inline (for posts) and featured (for homepage).
* Both quote the marketing site (open-and-async.com): a dark-navy "book
* object" anchored by the 3D cover mockup, with the lime→pink accent system.
* Now that the book has launched, both variants sell ("Buy it") rather than
* capture emails. For the dedicated launch announcement use BookLaunchCta.
*/

import { Image } from 'astro:assets';
Expand All @@ -30,10 +32,10 @@ const inlineHeadline =
relation === 'adapted'
? 'This post is adapted from the book.'
: relation === 'cut'
? "This post didn't make the book's final cut."
? "There's a whole book's worth more where this came from."
: relation === 'inspired'
? 'This post inspired a chapter in the book.'
: "Like this post? It's becoming a book.";
: "Liked this post? It's now a book.";

// Colorize the ampersand with the site's signature lime→pink gradient.
// Consume surrounding spaces so mx-1 controls the total whitespace (natural
Expand Down Expand Up @@ -62,7 +64,7 @@ const commitGraphPaths = `
target="_blank"
rel="noopener noreferrer"
data-track-book-cta
class="book-cta-featured not-prose block overflow-hidden rounded-xl bg-brand-950 border border-brand-600 p-6 md:p-8 relative group no-underline shadow-card dark:shadow-card-dark hover:shadow-card-hover dark:hover:shadow-card-hover-dark motion-safe:hover:-translate-y-px transition-all duration-200 motion-safe:animate-fade-up"
class="book-cta-featured not-prose block overflow-hidden rounded-xl bg-brand-950 border border-brand-600 p-6 md:p-8 relative group no-underline shadow-card dark:shadow-card-dark hover:shadow-card-hover dark:hover:shadow-card-hover-dark motion-safe:hover:-translate-y-px transition-all duration-200 motion-safe:animate-fade-up focus-visible:outline-2 focus-visible:outline-accent-400 focus-visible:outline-offset-2"
>
<svg class="pointer-events-none absolute inset-x-0 bottom-0 w-full h-10 opacity-40" viewBox="0 0 1200 60" fill="none" preserveAspectRatio="xMidYMid meet" aria-hidden="true" set:html={commitGraphPaths} />
<div class="relative flex flex-col md:flex-row md:items-center gap-6 md:gap-8">
Expand All @@ -75,23 +77,23 @@ const commitGraphPaths = `
densities={[1, 2]}
loading="lazy"
decoding="async"
class="rounded-md shadow-lg w-44 md:w-56 h-auto"
class="w-44 md:w-56 h-auto"
/>
</div>
<div class="flex-1 text-center md:text-left">
<p class="mb-3">
<span class="inline-flex items-center gap-1.5 rounded-md bg-brand-900 border border-brand-600 px-3 py-1 font-mono text-[0.7rem] uppercase tracking-widest text-white">
<span aria-hidden="true" class="text-accent-400">&gt;</span> Coming {siteConfig.bookLaunch}
<span aria-hidden="true" class="text-accent-400">&gt;</span> Out now
</span>
</p>
<p class="text-2xl md:text-3xl font-bold text-white mb-2 leading-tight">
<Fragment set:html={bookTitleHtml} />
</p>
<p class="text-sm md:text-base text-slate-300 mb-5">
{siteConfig.bookDescription}. Drawing on a decade at GitHub — sign up to get notified when it launches.
{siteConfig.bookDescription}. Drawing on a decade at GitHub — on Kindle, Apple Books, Kobo &amp; more.
</p>
<span class="oa-cta-btn inline-flex items-center gap-1.5 rounded-lg px-5 py-2.5 text-sm font-semibold text-brand-950 transition-all duration-200 whitespace-nowrap">
Get notified <span aria-hidden="true" class="motion-safe:group-hover:translate-x-0.5 transition-transform duration-200">→</span>
Buy it — {siteConfig.bookPrice} <span aria-hidden="true" class="motion-safe:group-hover:translate-x-0.5 transition-transform duration-200">→</span>
</span>
</div>
</div>
Expand All @@ -108,11 +110,11 @@ const commitGraphPaths = `
densities={[1, 2]}
loading="lazy"
decoding="async"
class="rounded-sm shadow-md w-16 sm:w-[72px] h-auto shrink-0"
class="w-16 sm:w-[72px] h-auto shrink-0"
/>
<div>
<p class="font-mono text-[0.65rem] uppercase tracking-widest text-accent-400 mb-1">
<span aria-hidden="true">&gt;</span> Coming {siteConfig.bookLaunch}
<span aria-hidden="true">&gt;</span> Out now
</p>
<p class="text-sm font-semibold text-white mb-1">
{inlineHeadline}
Expand All @@ -122,9 +124,9 @@ const commitGraphPaths = `
target="_blank"
rel="noopener noreferrer"
data-track-book-cta
class="inline-flex items-center gap-1.5 text-sm font-semibold text-accent-400 no-underline transition-colors duration-200 before:absolute before:inset-0 before:rounded-xl"
class="inline-flex items-center gap-1.5 text-sm font-semibold text-accent-400 no-underline transition-colors duration-200 before:absolute before:inset-0 before:rounded-xl focus-visible:outline-2 focus-visible:outline-accent-400 focus-visible:outline-offset-2"
>
Get notified when it launches <span aria-hidden="true" class="motion-safe:group-hover:translate-x-0.5 transition-transform duration-200">→</span>
Buy Open &amp; Async — {siteConfig.bookPrice} <span aria-hidden="true" class="motion-safe:group-hover:translate-x-0.5 transition-transform duration-200">→</span>
</a>
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/components/BookLaunchCta.astro
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const commitGraphPaths = `
densities={[1, 2]}
loading="lazy"
decoding="async"
class="mx-auto w-44 rounded-md shadow-2xl md:w-60 h-auto"
class="mx-auto w-44 md:w-60 h-auto"
/>
</div>

Expand All @@ -75,19 +75,19 @@ const commitGraphPaths = `

<div class="mt-7 flex flex-col sm:flex-row items-center gap-3 justify-center md:justify-start">
<a
href={siteConfig.bookUrl}
href={siteConfig.bookUrlLaunchPost}
target="_blank"
rel="noopener noreferrer"
class="oa-cta-btn group inline-flex w-full sm:w-auto items-center justify-center gap-2 rounded-xl px-7 py-3.5 text-base font-bold text-brand-950 transition-all duration-200"
class="oa-cta-btn group inline-flex w-full sm:w-auto items-center justify-center gap-2 rounded-xl px-7 py-3.5 text-base font-bold text-brand-950 transition-all duration-200 focus-visible:outline-2 focus-visible:outline-accent-400 focus-visible:outline-offset-2"
>
Buy the ebook — {siteConfig.bookPrice}
<span aria-hidden="true" class="transition-transform duration-200 group-hover:translate-x-0.5">&rarr;</span>
</a>
<a
href={siteConfig.bookUrl}
href={siteConfig.bookUrlLaunchPost}
target="_blank"
rel="noopener noreferrer"
class="inline-flex w-full sm:w-auto items-center justify-center gap-2 rounded-xl border border-brand-600 px-7 py-3.5 text-base font-semibold text-white no-underline transition-colors duration-200 hover:bg-brand-900"
class="inline-flex w-full sm:w-auto items-center justify-center gap-2 rounded-xl border border-brand-600 px-7 py-3.5 text-base font-semibold text-white no-underline transition-colors duration-200 hover:bg-brand-900 focus-visible:outline-2 focus-visible:outline-accent-400 focus-visible:outline-offset-2"
>
Paperback — {siteConfig.bookPricePaperback}
</a>
Expand Down
14 changes: 10 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,17 @@ export const siteConfig = {
// Find this in your Kit dashboard under Forms → form embed code.
kitFormId: '9381290',

// Book — pre-launch until bookLaunch. The sitewide BookCta stays in teaser
// mode ("Coming …", get-notified) until then; only the gated launch post's
// BookLaunchCta uses bookPrice/bookPricePaperback. On launch day, flip BookCta
// to buy-now to match the marketing site.
// Book — launched July 21, 2026. The sitewide BookCta, the launch post's
// BookLaunchCta, and the feed CTA all sell ("Buy it — bookPrice") to match
// the marketing site. bookLaunch is retained for reference/metadata.
//
// Each buy surface gets a distinct utm_source (matching the site-wide
// benbalter-<surface> convention used by the hero, footer, bio, and resume)
// so it's individually measurable: bookUrl = everyday sitewide CTA,
// bookUrlLaunchPost = the launch announcement post, bookUrlEmail = RSS/email.
bookUrl: 'https://open-and-async.com/?utm_source=benbalter-book-cta',
bookUrlLaunchPost: 'https://open-and-async.com/?utm_source=benbalter-launch-post',
bookUrlEmail: 'https://open-and-async.com/?utm_source=benbalter-email',
bookTitle: 'Open & Async',
bookDescription: 'The collaborative software development playbook for remote and distributed teams',
bookLaunch: 'July 21, 2026',
Expand Down
14 changes: 5 additions & 9 deletions src/content/posts/2026-07-21-open-and-async.mdx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
---
title: "Open and Async: the remote-work playbook is out today"
title: "Open and Async: the remote-work playbook is out"
description: "Open and Async is the practical playbook for making remote and distributed work actually work—two habits, working in the open and communicating asynchronously, drawn from a decade of remote-first lessons at GitHub."
tldr: "Open and Async is out today. It's the opinionated, practical playbook for the two habits that make distributed teams actually work—working in the open and communicating asynchronously. Drawn from a decade-plus at GitHub. Get it at open-and-async.com."
tldr: "Open and Async is out. It's the opinionated, practical playbook for the two habits that make distributed teams actually work—working in the open and communicating asynchronously. Drawn from a decade-plus at GitHub. Get it at [open-and-async.com](https://open-and-async.com/?utm_source=benbalter-launch-post)."
image: "https://open-and-async.com/og-image.png"
# Launch-day gate: keep this out of the build until July 21. Nothing filters on
# the post date, so without this the post (and its "out today" copy) would go
# live the moment this merges. Flip to true — or delete this line — and push on
# launch day to publish.
published: false
published: true
# This post supplies its own BookLaunchCta, so suppress the generic one that
# PostLayout appends to every post (avoids two buy-now CTAs back to back).
hideBookCta: true
---

Most companies didn't go remote. They shipped everyone a laptop, bolted Zoom onto the same approval chains, and called it a day. That wasn't remote work—it was office work in sweatpants: the same meetings, the same status theater, the same "quick syncs," just piped through a webcam.

**Working from home ≠ working remotely.** There's a world of difference between being forced out of the office and intentionally building a distributed, async culture. That difference is what *Open and Async* is about—and as of today, you can [read it](https://open-and-async.com/?utm_source=benbalter-launch-post).
:quote[Working from home ≠ working remotely.]{#work-from-home-not-remote} There's a world of difference between being forced out of the office and intentionally building a distributed, async culture. That difference is what *Open and Async* is about—and as of today, you can [read it](https://open-and-async.com/?utm_source=benbalter-launch-post).

[*Open and Async*](https://open-and-async.com/?utm_source=benbalter-launch-post) is the opinionated, practical playbook for the two practices that make distributed work actually work: **working in the open** and [**communicating asynchronously**](/2022/03/17/why-async/). Async is the operating system; remote is the hardware. It's how the next generation of tech leaders already think—and it's a learnable skill for everyone else.

Expand Down Expand Up @@ -46,6 +42,6 @@ A few of the things you'll learn how to do:

It's 575 pages, and the method ships as more than a manuscript. There's even a [Model Context Protocol (MCP) server](https://github.com/open-and-async/mcp) that drops the book's async-first practices straight into your editor, so an AI agent can draft a decision doc or pressure-test a status update against the same rubric the book teaches.

Stop digitizing the office. Start building something better.
:quote[Stop digitizing the office. Start building something better.]{#stop-digitizing-the-office}

<BookLaunchCta />
13 changes: 6 additions & 7 deletions src/pages/feed.xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,27 @@ function leadInHtml(link: string): string {
* so this is a self-contained equivalent. The headline mirrors BookCta's
* relation-aware copy (see src/components/BookCta.astro).
*
* LAUNCH (July 21, 2026): the "Coming …" label and "Get notified when it
* launches" CTA below are hardcoded and go stale at launch. When you flip
* <BookCta> to buy-now, update these two strings to match.
* Now that the book has launched, the label and CTA below sell ("Out now" /
* "Buy it") rather than capture emails — kept in sync with <BookCta>.
*/
function bookCtaHtml(relation?: 'adapted' | 'cut' | 'inspired'): string {
const headline =
relation === 'adapted'
? `This post is adapted from my book, ${bookTitle}.`
: relation === 'cut'
? `This one didn't make my book's final cut.`
? `There's a whole book's worth more where this came from.`
: relation === 'inspired'
? `This post inspired a chapter in my book, ${bookTitle}.`
: `Like this post? It's becoming a book.`;
: `Liked this post? It's now a book.`;

return (
`<hr style="margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;" />` +
`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 1em;border-collapse:collapse;">` +
`<tr><td style="border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;">` +
`<p style="margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;">Coming ${siteConfig.bookLaunch}</p>` +
`<p style="margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;">Out now</p>` +
`<p style="margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;">${headline}</p>` +
`<p style="margin:0 0 12px;font-size:14px;color:#424a53;">${siteConfig.bookDescription}.</p>` +
`<a href="${siteConfig.bookUrl}" style="font-size:14px;font-weight:600;color:#0969da;text-decoration:none;">Get notified when it launches &rarr;</a>` +
`<a href="${siteConfig.bookUrlEmail}" style="font-size:14px;font-weight:600;color:#0969da;text-decoration:none;">Buy it — ${siteConfig.bookPrice} &rarr;</a>` +
`</td></tr></table>`
);
}
Expand Down
Loading