diff --git a/assets/img/header.jpg b/assets/img/header.jpg index 734dbf018..1b3639c3b 100644 Binary files a/assets/img/header.jpg and b/assets/img/header.jpg differ diff --git a/public/assets/img/header.jpg b/public/assets/img/header.jpg index dd6e4029e..febe8ce78 100644 Binary files a/public/assets/img/header.jpg and b/public/assets/img/header.jpg differ diff --git a/src/__tests__/pages/feed.xml.test.ts b/src/__tests__/pages/feed.xml.test.ts index 0276ff79d..56a967724 100644 --- a/src/__tests__/pages/feed.xml.test.ts +++ b/src/__tests__/pages/feed.xml.test.ts @@ -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('

Rendered HTML

'); 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('

Rendered HTML

'), ); expect(item.content.indexOf('

Rendered HTML

')).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); diff --git a/src/components/BookCta.astro b/src/components/BookCta.astro index 6c5b95759..0b660915c 100644 --- a/src/components/BookCta.astro +++ b/src/components/BookCta.astro @@ -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'; @@ -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 @@ -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" >
@@ -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" />

- Coming {siteConfig.bookLaunch} + Out now

- {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 & more.

- Get notified + Buy it — {siteConfig.bookPrice}
@@ -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" />

- Coming {siteConfig.bookLaunch} + Out now

{inlineHeadline} @@ -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 + Buy Open & Async — {siteConfig.bookPrice}

diff --git a/src/components/BookLaunchCta.astro b/src/components/BookLaunchCta.astro index 0b907bacc..d8a68ccb0 100644 --- a/src/components/BookLaunchCta.astro +++ b/src/components/BookLaunchCta.astro @@ -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" /> @@ -75,19 +75,19 @@ const commitGraphPaths = `
Buy the ebook — {siteConfig.bookPrice} Paperback — {siteConfig.bookPricePaperback} diff --git a/src/config.ts b/src/config.ts index 0e63ce28e..f17e937aa 100644 --- a/src/config.ts +++ b/src/config.ts @@ -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- 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', diff --git a/src/content/posts/2026-07-21-open-and-async.mdx b/src/content/posts/2026-07-21-open-and-async.mdx index eb8c8ffeb..f529cb229 100644 --- a/src/content/posts/2026-07-21-open-and-async.mdx +++ b/src/content/posts/2026-07-21-open-and-async.mdx @@ -1,13 +1,9 @@ --- -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 @@ -15,7 +11,7 @@ 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. @@ -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} diff --git a/src/pages/feed.xml.ts b/src/pages/feed.xml.ts index 492d1f112..d008dad5b 100644 --- a/src/pages/feed.xml.ts +++ b/src/pages/feed.xml.ts @@ -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 - * 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 . */ 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 ( `
` + `` + `
` + - `

Coming ${siteConfig.bookLaunch}

` + + `

Out now

` + `

${headline}

` + `

${siteConfig.bookDescription}.

` + - `Get notified when it launches →` + + `Buy it — ${siteConfig.bookPrice} →` + `
` ); }