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
1 change: 1 addition & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export default defineConfig({
'./src/components/GitHubCulture.astro',
'./src/components/FossAtScale.astro',
'./src/components/YouTube.astro',
'./src/components/BookLaunchCta.astro',
// astro-embed components for zero-JS social embeds
{
'astro-embed': ['Tweet', 'Vimeo', 'LinkPreview'],
Expand Down
16 changes: 9 additions & 7 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 Down Expand Up @@ -63,17 +65,17 @@ const commitGraphPaths = `
<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 — out now 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 @@ -94,18 +96,18 @@ const commitGraphPaths = `
/>
<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">
Like this post? It's becoming a book.
Liked this post? It's now a book.
</p>
<a
href={siteConfig.bookUrl}
target="_blank"
rel="noopener noreferrer"
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"
>
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
136 changes: 136 additions & 0 deletions src/components/BookLaunchCta.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
/**
* Book Launch CTA — the bespoke, high-impact "buy now" panel reserved for the
* launch announcement post. Louder than BookCta's inline/featured variants: a
* glowing lime→pink gradient ring around a full-width navy panel, the 3D cover,
* an oversized title, and dual buy buttons (ebook + paperback). Use BookCta for
* the everyday inline/homepage promos; use this once, on the launch post.
*/

import { Image } from 'astro:assets';
import { siteConfig } from '../config';
import coverImage from '../../assets/img/open-and-async-cover.webp';

// Reuse the marketing site's gradient-ampersand treatment for the title.
const bookTitleHtml = siteConfig.bookTitle.replace(
/\s*&\s*/g,
'<span class="oa-amp mx-1">&amp;</span>',
);

// Commit-graph motif (matches BookCta): faint branch graph along the bottom.
const commitGraphPaths = `
<path d="M0 30 H 1200" stroke="var(--color-accent-400)" stroke-width="2.5"/>
<path d="M340 30 C 420 30, 440 12, 520 12 S 680 30, 760 30" stroke="var(--color-pink-400)" stroke-width="2"/>
<path d="M620 30 C 700 30, 720 48, 800 48 S 940 36, 1000 30" stroke="var(--color-accent-400)" stroke-width="2"/>
<circle cx="240" cy="30" r="6" fill="var(--color-accent-400)"/>
<circle cx="520" cy="12" r="5" fill="var(--color-pink-400)"/>
<circle cx="800" cy="48" r="5" fill="var(--color-accent-400)"/>
<circle cx="1000" cy="30" r="8" fill="none" stroke="var(--color-accent-400)" stroke-width="2.5"/>
`;
---

<div class="book-launch-cta not-prose my-12 motion-safe:animate-fade-up">
<!-- Gradient ring wrapper: 2px lime→pink frame with a soft outer glow. -->
<div class="oa-ring rounded-2xl p-[2px] shadow-card dark:shadow-card-dark">
<div class="relative overflow-hidden rounded-2xl bg-brand-950 px-6 py-8 md:px-10 md:py-10">
<svg
class="pointer-events-none absolute inset-x-0 bottom-0 w-full h-12 opacity-40"
viewBox="0 0 1200 60"
fill="none"
preserveAspectRatio="xMidYMid meet"
aria-hidden="true"
set:html={commitGraphPaths}
/>

<div class="relative flex flex-col items-center gap-8 text-center md:flex-row md:items-center md:gap-10 md:text-left">
<!-- Cover -->
<div class="shrink-0">
<Image
src={coverImage}
alt="Open & Async by Ben Balter — book cover"
width={240}
height={266}
densities={[1, 2]}
loading="lazy"
decoding="async"
class="mx-auto w-44 rounded-md shadow-2xl md:w-60 h-auto"
/>
</div>

<!-- Copy + actions -->
<div class="flex-1">
<p class="mb-4">
<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-xs uppercase tracking-widest text-white">
<span aria-hidden="true" class="text-accent-400">&gt;</span> Out now &middot; {siteConfig.bookLaunch}
</span>
</p>

<p class="text-3xl md:text-5xl font-bold leading-[1.05] text-white">
<Fragment set:html={bookTitleHtml} />
</p>

<p class="mt-4 text-base md:text-lg text-slate-300 max-w-xl mx-auto md:mx-0">
{siteConfig.bookDescription}.
</p>

<div class="mt-7 flex flex-col sm:flex-row items-center gap-3 justify-center md:justify-start">
<a
href={siteConfig.bookUrl}
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"
>
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}
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"
>
Paperback — {siteConfig.bookPricePaperback}
</a>
</div>

<p class="mt-4 text-sm text-slate-400">
One link, every store — Kindle, Apple Books, Kobo &amp; more.
</p>
</div>
</div>
</div>
</div>
</div>

<style>
@reference "../styles/global.css";

/* Gradient frame + soft outer glow so the panel reads as the page's focal
point. The glow is the same lime→pink accent system, dialed back. */
.oa-ring {
background: linear-gradient(135deg, var(--color-accent-400), var(--color-pink-400));
box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-accent-400) 30%, transparent),
0 18px 50px -12px color-mix(in srgb, var(--color-pink-400) 45%, transparent);
}

/* Signature lime→pink gradient text (ampersand), with a solid fallback so the
character survives where background-clip:text is unsupported. */
.oa-amp {
color: var(--color-accent-400);
background: linear-gradient(to right, var(--color-accent-400), var(--color-pink-400));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}

/* Primary button: lime→pink fill with a navy label (navy passes WCAG on the
accent fill where white would fail). */
.oa-cta-btn {
background: linear-gradient(to right, var(--color-accent-400), var(--color-pink-400));
text-decoration: none;
}

.oa-cta-btn:hover {
filter: brightness(1.05);
}
</style>
5 changes: 4 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,14 @@ export const siteConfig = {
// Find this in your Kit dashboard under Forms → form embed code.
kitFormId: '9381290',

// Book
// Book — now launched, so the callouts sell ("buy now") rather than capture
// emails. bookPrice leads the CTAs; paperback is the secondary option.
bookUrl: 'https://open-and-async.com/',
bookTitle: 'Open & Async',
bookDescription: 'The collaborative software development playbook for remote and distributed teams',
bookLaunch: 'July 21, 2026',
bookPrice: '$9.99',
bookPricePaperback: '$29.99',

// About page metadata
aboutDescription: 'Ben Balter is a writer, open source developer, and attorney whose open source projects have hundreds of millions of downloads. Formerly Director of Hubber Enablement at GitHub.',
Expand Down
3 changes: 2 additions & 1 deletion src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const postsCollection = defineCollection({

// Post metadata
image: z.string().optional(), // Open Graph image

hideBookCta: z.boolean().default(false), // Suppress the auto-appended BookCta (e.g. the launch post supplies its own BookLaunchCta)

// SEO metadata
sitemap: z.boolean().default(true), // Include in sitemap by default
robots: z.string().optional(), // Robots meta tag
Expand Down
46 changes: 46 additions & 0 deletions src/content/posts/2026-07-21-open-and-async.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "Open and Async: the remote-work playbook is out today"
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."
image: "https://open-and-async.com/og-image.png"
# 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).

[*Open and Async*](https://open-and-async.com) 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.

If you've spent any time on this blog, the argument will feel familiar—the book grew out of these posts. It's where the scattered pieces finally become one ordered playbook: the full case, start to finish, with the connective tissue no single post ever had room for. Everything you've read here, plus the two-thirds that never made it to the blog.

## What separates teams that thrive

More than a decade of remote-first work at GitHub made one thing clear: the tooling was never what separated the teams that thrived from the ones that just survived. Companies would adopt those workflows wholesale—add issues and pull requests on top of a hierarchy-driven approval process—and wonder why nothing changed. Tools: right. Bottlenecks: intact. The result was Zoom fatigue plus all the old dysfunction, now faster.

The teams that pulled ahead did something different. They gave every decision [a URL](/2015/11/12/why-urls/) so context survived reorgs and departures. They treated meetings as escalation, not default. They [worked loudly](/2022/02/16/leaders-show-their-work/) so impact was visible without anyone performing busyness. None of it was intuition—it was a set of repeatable practices, and *Open and Async* is those practices, written down so you can put them to work.

## Who it's for

Two audiences, both in remote and distributed work:

* **Managers**—build teams that ship without constant synchronous coordination, and retain your best people by respecting their time, focus, and intelligence.
* **Individual contributors**—become visible, effective, and promotable on the strength of your work, not the luck of your seating chart. Interviewing at a remote-first company? This is the playbook they wish you'd already read.

## What's inside

A few of the things you'll learn how to do:

* Treat meetings as a point of escalation, not the default—and reclaim the hours you're losing to "quick syncs."
* Give every decision a URL so context survives reorgs, departures, and Slack purges.
* Run 1:1s, weekly reports, and career conversations that aren't a tax on everyone's time.
* Lead—whether or not you have direct reports—by showing your work.
* Hire, onboard, and transition teams to remote-first work without losing what made them good.

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.

<BookLaunchCta />
4 changes: 3 additions & 1 deletion src/layouts/PostLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface Props {
prevPost?: { title: string; url: string; readingTime?: number } | undefined;
nextPost?: { title: string; url: string; readingTime?: number } | undefined;
archived?: boolean;
hideBookCta?: boolean;
postId: string;
postFilePath?: string | undefined;
posts?: Record<string, string> | string[] | undefined;
Expand All @@ -60,6 +61,7 @@ const {
prevPost,
nextPost,
archived = false,
hideBookCta = false,
postId,
postFilePath,
posts: curatedPosts,
Expand Down Expand Up @@ -149,7 +151,7 @@ const blogPostingSchema = pubDate ? generateBlogPostingSchema({

<DiscussLinks title={title} url={postUrl} />

<BookCta />
{!hideBookCta && <BookCta />}

<KeepReading relatedPosts={relatedPosts} />

Expand Down
1 change: 1 addition & 0 deletions src/pages/[year]/[month]/[day]/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const nextPost = nextEntry ? { title: nextEntry.data.title, url: getPostUrl(next
prevPost={prevPost}
nextPost={nextPost}
archived={post.data.archived}
hideBookCta={post.data.hideBookCta}
postId={post.id}
postFilePath={post.filePath}
posts={post.data.posts}
Expand Down
Loading