Tawny

Live design demo

Art

A bilingual bento journal with MDX notes, projects, and interactive demos — open the live showcase. Navigate inside the frame — you are still on Tawny.

designs/art

June 7, 2026

The black gap

How the home page bento grid calculates its black lines for responsive layouts — one CSS token, no magic borders.

Read in 中文

Look closely at the home page and you will notice thin black lines between every tile. They are not borders drawn on the cells — they are gaps showing through to the black background behind the grid.

Once you see the trick, you can reproduce it anywhere: light cells on a dark canvas, with spacing as the only divider.

One token controls everything

The entire system hangs on a single CSS custom property in globals.css:

app/globals.css
@theme {
--spacing-bento-gap: 8px;
}

@media (min-width: 768px) {
:root {
  --spacing-bento-gap: 16px;
}
}

Tailwind v4 maps that token to utilities like gap-bento-gap and p-bento-gap. Change the variable once and every grid, padding, and carousel accent updates together.

How the grid works

The home bento in home-bento.tsx stacks three layers:

  1. Outer shellbg-black p-bento-gap wraps the whole grid. The padding creates a black frame around the edge.
  2. Main gridgap-bento-gap bg-black on a 12-column layout. Gap space is transparent, so the black background shows through between cells.
  3. Cells — each tile uses bg-bento-bg (a warm off-white). Only the cells are light; everything between them stays black.

The author / social block uses the same pattern as a nested sub-grid: gap-bento-gap bg-black inside a parent cell, so inner gaps match outer gaps exactly.

Carousel prev/next buttons in image-carousel.tsx reuse the token too — their border width is set to var(--spacing-bento-gap) so control chrome aligns with grid lines.

RWD reordering

On mobile the grid collapses to a single column. Tiles reorder with Tailwind order-* utilities so the reading flow makes sense (CTA and hero image land in sensible positions) while the gap token stays the same.

On desktop (md:grid-cols-12) tiles snap into a strict 50/50 split: 6 + 6 columns per row, with explicit col-span-* and row-start-* placement.

BreakpointGapGrid
< 768px8px1 column, CSS order
≥ 768px16px12 columns, 3 rows

Try the gap yourself

Drag the slider or tap a preset to see how gap size changes the bento feel:

Adjust the gap

Mini bento preview

A
B
C
D

What is next

Return to the home page and resize your browser — watch the gaps step from 8px to 16px at the breakpoint. For the full stack behind this site, read Building this journal.

About

Art is a bilingual personal journal built around a brutalist bento home grid. It ships with MDX notes and projects, next-intl for English and Chinese, and interactive tiles — weather, location globe, image and tag carousels.

Use the live demo above to explore the full experience inside Tawny. When you are ready to start from the same foundation, scaffold a copy with the create command below, then replace the placeholder author details and sample content.

Get the template

Scaffold a local copy with your preferred package manager.

Terminal
pnpm create tawny@latest art my-app

Template source

Browse every file in templates/art.

templates/art

README.md
# Bento Journal

A bilingual personal journal template — brutalist bento home grid, MDX posts, and an editorial reading experience.

Design by [Hugo Lin](https://1chooo.com).

## Quick start

### Option A — create-next-app (recommended)

```bash
npx create-next-app@latest my-journal \
  --example "https://github.com/1chooo/tawny" \
  --example-path "templates/art"
```

### Option B — create-tawny CLI (interactive prompts)

```bash
npx create-tawny@latest art my-journal
```

Then:

```bash
cd my-journal
pnpm install   # or npm / yarn / bun
cp .env.example .env   # optional: set WEATHER_CITY
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000).

## Start here (sample content)

After scaffolding, read these starter articles in order:

1. **[Hello, world](/en/notes/hello-world)** — tone and basic MDX
2. **[Writing in MDX](/en/notes/writing-in-mdx)**`postMeta`, images, shortcodes (`Callout`, `CodeBlock`, `TagDemo`, `GapDemo`, etc.)
3. **[Sample Project](/en/projects/sample-project)** — how featured projects appear on the home grid

Delete or replace them once you are comfortable.

## Customize these files

| File | What to change |
|------|----------------|
| `messages/en.json`, `messages/zh.json` | Site title, brand name, about page copy, nav labels |
| `lib/bento-links.ts` | GitHub, email, and location quick links on the home grid |
| `lib/locations.ts` | Cities on the globe page (coordinates + ids) |
| `lib/nav.ts` | Navigation items |
| `content/posts/` | Your notes (`.mdx` files with `postMeta` export) |
| `content/projects/` | Your projects (`projectMeta` export) |
| `public/` | Images — `opengraph-image.png` powers the bento carousel and sample MDX |
| `.env` | `WEATHER_CITY` or `WEATHER_LAT` / `WEATHER_LON` for the home weather tile |

### Placeholders (if you used Option A)

Search for `{{AUTHOR_NAME}}`, `{{AUTHOR_EMAIL}}`, `{{GITHUB_USERNAME}}`, and `{{CITY_NAME}}` across the project and replace them, or use Option B which does this automatically.

## Adding a post

Create `content/posts/my-post.mdx`:

```mdx
export const postMeta = {
  title: "My first note",
  date: "2026-06-06",
  description: "A short summary for listings and SEO.",
  tags: ["journal"],
};

Your MDX content starts here.
```

No manual registry — `lib/posts.ts` scans `content/posts/` at build time.

- `my-post.mdx` → English only
- `my-post.en.mdx` + `my-post.zh.mdx` → bilingual under the same slug

Visit `/en/notes/my-post` (or `/zh/notes/my-post`).

## Stack

- Next.js 16 App Router & React Server Components
- MDX with remark-gfm
- Tailwind CSS v4 & @tailwindcss/typography
- next-intl (English & Chinese)
- Bento home grid with image, tag, and upcoming carousels

## License

MIT — see [LICENSE](./LICENSE).