Skip to content
Draft
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
19 changes: 19 additions & 0 deletions .github/workflows/design-system.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Design system
on:
pull_request:
paths: ['desktop-next/**', 'packages/**', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', '.github/workflows/design-system.yml']
push:
branches: [design-system]
paths: ['desktop-next/**', 'packages/**', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', '.github/workflows/design-system.yml']
permissions:
contents: read
jobs:
design-system:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616
- run: pnpm install --filter buzz-desktop-next... --frozen-lockfile
- run: pnpm --filter buzz-desktop-next check
- run: pnpm --filter buzz-desktop-next build
10 changes: 6 additions & 4 deletions desktop-next/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,8 @@ time as the product repeats something — see DESIGN.md § Components.

Deliberately, so nobody assumes it was forgotten:

- **Spacing, radius, and motion tokens.** Their `/design` pages state what is
still to decide rather than pretending to a system. Typography has landed;
spacing is next.
- **Any product component.** No Button, no Dialog, no input layer.
- **Product integration.** Tokens and shared primitives are built before product surfaces.
- **Relay-backed product workflows.** The shared component catalog uses local fixtures.
- **Tauri.** This is a web app for now; the native shell comes with the app shell.
- **Relay, auth, event handling.** None of it. When it arrives it comes from the
shared Rust crates, not a reimplementation.
Expand All @@ -223,3 +221,7 @@ Two rules that matter from day one:
- **Do not create a capability speculatively.** The bar is a durable product
identity and real composition pressure from two surfaces. Building a clean
codebase is not a licence to relax it.

## Portable foundations

Color and typography source live in packages/design-tokens/src; desktop style files import them. The local registry re-exports the package registry. Geometry and motion derive from foundations.ts through build.mjs. Shared components are intentionally built before product adoption; the original repeated-use threshold does not apply to this foundational catalog.
6 changes: 5 additions & 1 deletion desktop-next/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Buzz is a place where people build together and bring their agents into the room

## State

- **The interface has three states, plus disabled where it matters: default, hover, selected.** There is no pressed state: pressed is too fleeting to read and makes an interface feel jumpy.
- **The interface has three states, plus disabled where it matters: default, hover, selected.** Buttons may use the shared subtle pressed feedback token. Reduced motion removes its scale change; selected remains distinct from pressed.
- **Hover means one step more contrast, in whichever direction that surface needs.** A light row darkens, a dark chip lightens. Direction lives in the value.
- **Selected is a persistent statement, not a stronger hover.** It should be legible without a cursor present.
- **A selected item in a toggle group is not interactive.** Clicking it does nothing, so it gets no hover.
Expand Down Expand Up @@ -179,3 +179,7 @@ Every addition lands in `src/shared/tokens/registry.ts` in the same change that
- **Use an existing component before creating one, and an existing role before adding one.**
- **A new visual treatment that repeats belongs in the system, not in the feature.**
- **If a screen looks right but breaks these rules, the rules are probably wrong — say so.** This document is meant to be argued with, not worked around.

## Open-source component foundation

The shared component library is built proactively for the new client. Public Base UI provides behavior; Buzz owns styling, tokens, and named variants. Use Inter, JetBrains Mono, and Lucide. See OPEN_SOURCE.md for the adaptation contract.
19 changes: 19 additions & 0 deletions desktop-next/OPEN_SOURCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Open-source design system

This implementation is authored for Buzz under the repository Apache-2.0 license. It uses public Base UI behavior primitives, Inter Variable, JetBrains Mono, and Lucide. Their original licenses remain applicable and must accompany redistributed dependencies.

The visual starting point uses neutral surfaces, pill actions, role-based geometry, and clear content hierarchy. No proprietary font files, icon sets, internal screenshots, Figma snapshots, private source files, or internal service clients are included. References informed visual decisions; this is an independently authored implementation, not an export of an internal repository.

## Intentional adaptations

- Inter replaces proprietary sans typography; JetBrains Mono covers code.
- Lucide provides interface icons. Illustrations and examples are original or synthetic.
- Buzz conversation type retains its 13/14/15 preference contract and rem-based zoom.
- Contrast takes priority over exact reference colors.
- The app backdrop and glass remain Buzz materials; dense reading sits on panels.
- Components are now built as a public foundation. Press feedback is tokenized and respects reduced motion.
- CSS and local registries are authoritative. Neither Figma nor any internal service is required to build or run.

## Asset licenses

Inter and JetBrains Mono: SIL Open Font License 1.1. Lucide: ISC, with MIT notices for Feather-derived icons. Base UI: MIT. Preserve the license files distributed with these dependencies. New Buzz source remains Apache-2.0.
12 changes: 8 additions & 4 deletions desktop-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"build": "vite build && tsc --noEmit",
"typecheck": "tsc --noEmit",
"preview": "vite preview",
"lint": "biome lint .",
"check": "biome check . && node scripts/check-type.mjs && node scripts/check-contrast.mjs",
"check": "biome check . && node scripts/check-type.mjs && node scripts/check-contrast.mjs && pnpm check:tokens && pnpm test",
"format": "biome format --write .",
"check:type": "node scripts/check-type.mjs",
"check:contrast": "node scripts/check-contrast.mjs"
"check:contrast": "node scripts/check-contrast.mjs",
"check:tokens": "node ../packages/design-tokens/build.mjs --check",
"tokens:build": "node ../packages/design-tokens/build.mjs",
"test": "pnpm --filter @buzz/design-tokens test"
},
"dependencies": {
"@base-ui-components/react": "1.0.0-rc.0",
"@fontsource-variable/inter": "^5.2.8",
"@fontsource/jetbrains-mono": "^5.3.0",
"@tanstack/react-router": "^1.168.10",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react-dom": "^19.1.0",
"@buzz/design-tokens": "workspace:*"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.3.0",
Expand Down
2 changes: 1 addition & 1 deletion desktop-next/scripts/check-contrast.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { fileURLToPath } from "node:url";
import { apcaContrast, wcagRatio } from "./apca.mjs";

const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
const TOKENS = join(ROOT, "src/shared/styles/tokens.css");
const TOKENS = join(ROOT, "../packages/design-tokens/src/tokens.css");

/** Body text. Anything a person must read to use the product. */
const TARGET_BODY = 60;
Expand Down
46 changes: 13 additions & 33 deletions desktop-next/src/features/design-system/ui/MotionPage.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
import { PageHeader, Section, Stub } from "./primitives";

import { FOUNDATION_GROUPS } from "@buzz/design-tokens/foundations";
import { PageHeader, Section } from "./primitives";
export function MotionPage() {
const group = FOUNDATION_GROUPS[2];
return (
<>
<PageHeader
title="Motion"
status="not started"
intro="No durations or easings are defined yet. The rules that govern them already exist in DESIGN.md, which is the harder half."
/>

<Section title="What this page will hold">
<Stub
what="Duration and easing tokens, and the named roles that pair them: a state change, an element entering, an overlay opening, a drag settling."
decide={[
"The duration steps, and which is the default",
"Easing curves, and which motions get a spring rather than a curve",
"How reduced-motion preferences are honoured at the token level rather than per component",
]}
/>
</Section>

<Section
title="Rules that already apply"
description="These are in DESIGN.md and do not wait for the tokens."
>
<ul className="flex list-disc flex-col gap-2 pl-5">
{[
"Direct manipulation follows the pointer exactly, with no easing — smoothing during a drag reads as lag. Spring physics belongs to what happens after release.",
"Never animate blur. Re-blurring a large surface every frame is expensive enough to feel. Animate opacity instead.",
"Motion explains a change; it does not decorate one. If removing an animation loses no information, remove it.",
].map((rule) => (
<li key={rule} className="text-body text-secondary">
{rule}
</li>
<PageHeader title={group.name} intro={group.description} />
<Section title="Roles">
<dl className="grid gap-6">
{group.roles.map(([name, value, use]) => (
<div key={name} className="grid gap-2 sm:grid-cols-3">
<dt className="text-code font-mono">{name}</dt>
<dd className="text-body text-secondary">{value}</dd>
<dd className="text-caption text-secondary">{use}</dd>
</div>
))}
</ul>
</dl>
</Section>
</>
);
Expand Down
31 changes: 14 additions & 17 deletions desktop-next/src/features/design-system/ui/RadiusPage.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { PageHeader, Section, Stub } from "./primitives";

import { FOUNDATION_GROUPS } from "@buzz/design-tokens/foundations";
import { PageHeader, Section } from "./primitives";
export function RadiusPage() {
const group = FOUNDATION_GROUPS[1];
return (
<>
<PageHeader
title="Radius"
status="not started"
intro="The design exploration is consistently soft — rounded panels, pill-shaped chrome, gently rounded chips — but the specific values have not been pulled into a scale yet."
/>

<Section title="What this page will hold">
<Stub
what="A radius scale, and the named roles that point at it: the panel corner, the control corner, the chip corner, and the fully-round pill."
decide={[
"How many steps the scale needs",
"Which roles exist, and what each one is for",
"Whether nested corners follow a rule, so a control inside a panel reads correctly",
]}
/>
<PageHeader title={group.name} intro={group.description} />
<Section title="Roles">
<dl className="grid gap-6">
{group.roles.map(([name, value, use]) => (
<div key={name} className="grid gap-2 sm:grid-cols-3">
<dt className="text-code font-mono">{name}</dt>
<dd className="text-body text-secondary">{value}</dd>
<dd className="text-caption text-secondary">{use}</dd>
</div>
))}
</dl>
</Section>
</>
);
Expand Down
32 changes: 14 additions & 18 deletions desktop-next/src/features/design-system/ui/SpacingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { PageHeader, Section, Stub } from "./primitives";

import { FOUNDATION_GROUPS } from "@buzz/design-tokens/foundations";
import { PageHeader, Section } from "./primitives";
export function SpacingPage() {
const group = FOUNDATION_GROUPS[0];
return (
<>
<PageHeader
title="Spacing"
status="not started"
intro="Tailwind's default spacing scale is currently in play, unexamined. Whether that is the right rhythm for this design is an open decision."
/>

<Section title="What this page will hold">
<Stub
what="A spacing scale with the same two-layer treatment as colour: raw steps below, and named roles for the spacings that carry meaning — the gutter between rows, the inset of a panel, the gap inside a control."
decide={[
"Whether the stock scale stays or the design needs its own rhythm",
"Which spacings are named roles rather than raw steps",
"Whether density varies per surface, and if so how that is expressed",
"The relationship between spacing and the type ramp",
]}
/>
<PageHeader title={group.name} intro={group.description} />
<Section title="Roles">
<dl className="grid gap-6">
{group.roles.map(([name, value, use]) => (
<div key={name} className="grid gap-2 sm:grid-cols-3">
<dt className="text-code font-mono">{name}</dt>
<dd className="text-body text-secondary">{value}</dd>
<dd className="text-caption text-secondary">{use}</dd>
</div>
))}
</dl>
</Section>
</>
);
Expand Down
2 changes: 2 additions & 0 deletions desktop-next/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "@fontsource-variable/inter/index.css";
import "@fontsource/jetbrains-mono/index.css";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider, createRouter } from "@tanstack/react-router";
Expand Down
3 changes: 1 addition & 2 deletions desktop-next/src/shared/styles/globals.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
@import "tailwindcss";
@import "@fontsource-variable/inter";
@import "@fontsource/jetbrains-mono";
@import "./tokens.css";
@import "./typography.css";
@import "@buzz/design-tokens/foundations.css";

@layer base {
body {
Expand Down
Loading
Loading