From f98532f0a0b15d169c0f1c534cc89e68d892b302 Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Sat, 18 Jul 2026 23:23:14 -0700 Subject: [PATCH] refine(desktop): interlocking honeycomb for Community Home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design refinement pass on the Community Home surface (visual only — nullable selection, join/create/remove, and navigation are unchanged): - Rebuild the hex grid as a true interlocking honeycomb: pointy-top hexes that tessellate, ResizeObserver-driven sizing, and balanced rows (e.g. 5 tiles render 3+2, not 4+1) instead of a staggered grid with large gaps. - Fix hierarchy: communities now read as elevated hero tiles; join and create are lighter, additive cells rather than the heaviest elements. - Square the icon tiles, strengthen tile contrast/borders, and use a clip-path-safe drop shadow for real elevation. - Polish typography, hover/focus affordances (lift + primary outline + "Enter" hint), and add ease-out motion with reduced-motion fallbacks. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Bradley Axen --- .../features/communities/ui/CommunityHome.tsx | 239 +++++++++++++----- 1 file changed, 178 insertions(+), 61 deletions(-) diff --git a/desktop/src/features/communities/ui/CommunityHome.tsx b/desktop/src/features/communities/ui/CommunityHome.tsx index 6583398326..25b6d801a0 100644 --- a/desktop/src/features/communities/ui/CommunityHome.tsx +++ b/desktop/src/features/communities/ui/CommunityHome.tsx @@ -5,8 +5,8 @@ import * as React from "react"; import { ThemeGrainientBackground } from "@/app/ThemeGrainientBackground"; import type { Community } from "@/features/communities/types"; import { useCommunityIcons } from "@/features/communities/useCommunityIcons"; +import { useElementWidth } from "@/shared/hooks/use-mobile"; import { getInitials } from "@/shared/lib/initials"; -import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; import { AlertDialog, @@ -28,41 +28,98 @@ import { import { StartupWindowDragRegion } from "@/shared/ui/StartupWindowDragRegion"; const CREATE_COMMUNITY_URL = "https://app.builderlab.xyz/signup?returnTo=/buzz"; + +// Pointy-top hexagon: flat vertical sides (so hexes in a row touch along their +// edges) and points at top/bottom (so alternating rows nest into each other). const HEX_CLIP_PATH = - "polygon(25% 6.7%, 75% 6.7%, 100% 50%, 75% 93.3%, 25% 93.3%, 0 50%)"; + "polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)"; + +// Pointy-top geometry, as ratios of the hex width W. +// height = W / 0.866 (≈ 1.1547·W) +// row nest = rows overlap vertically by 1/4 of the hex height. +const HEX_ASPECT = 0.866; // width / height +const ROW_OVERLAP = 0.2887; // 0.25 · (1 / 0.866) — negative margin between rows + +/** Pick a hex width + column cap that keeps regular hexagons at any width. */ +function honeycombLayout(containerWidth: number): { + hexWidth: number; + maxColumns: number; +} { + if (containerWidth >= 860) return { hexWidth: 176, maxColumns: 4 }; + if (containerWidth >= 640) return { hexWidth: 156, maxColumns: 4 }; + if (containerWidth >= 440) return { hexWidth: 140, maxColumns: 3 }; + return { hexWidth: 118, maxColumns: 2 }; +} + +/** Split items into honeycomb rows given how many fit per row. */ +function chunkRows(items: T[], perRow: number): T[][] { + const rows: T[][] = []; + for (let i = 0; i < items.length; i += perRow) { + rows.push(items.slice(i, i + perRow)); + } + return rows; +} + +/** Shared shell: a fixed-width regular hexagon with a hairline edge + fill. */ +function HexShell({ + children, + edgeClassName, + fillClassName, + width, +}: { + children: React.ReactNode; + edgeClassName: string; + fillClassName: string; + width: number; +}) { + return ( + <> + } + key="__create" + label="Create a community" + onClick={() => void openUrl(CREATE_COMMUNITY_URL)} + testId="community-home-create" + width={hexWidth} + /> + ), + }, + ]; + + const rows = chunkRows(tiles, perRow); + const hasStagger = rows.length > 1; return (
Buzz communities

-

+

Where do you want to go?

@@ -195,42 +316,38 @@ export function CommunityHome({

*:nth-child(even)]:translate-y-[45%] sm:[&>*:nth-child(even)]:translate-y-0", - "sm:[&>*:nth-child(3n+2)]:translate-y-[45%] md:[&>*:nth-child(3n+2)]:translate-y-0", - "md:[&>*:nth-child(even)]:translate-y-[45%]", - )} + className="mt-14 flex flex-col items-center" + ref={gridRef} > - {communities.map((community) => ( - onOpenCommunity(community.id)} - onRemove={() => setCommunityToRemove(community)} - /> - ))} - } - label="Join a community" - onClick={onJoinCommunity} - testId="community-home-join" - /> - ✦} - label="Create a community" - onClick={() => void openUrl(CREATE_COMMUNITY_URL)} - testId="community-home-create" - /> + {rows.map((row, rowIndex) => { + const isOffsetRow = rowIndex % 2 === 1; + // Nest each row up into the previous one and stagger alternating + // rows by half a hex so the pointed edges interlock. + const translateX = hasStagger + ? isOffsetRow + ? hexWidth / 4 + : -hexWidth / 4 + : 0; + return ( +
tile.key).join("|")} + style={{ + marginTop: + rowIndex === 0 ? 0 : -(hexWidth / HEX_ASPECT) * ROW_OVERLAP, + transform: `translateX(${translateX}px)`, + }} + > + {row.map((tile) => tile.node)} +
+ ); + })}
{communities.length === 0 ? ( -

- This is your community home. It will stay available whenever you - want a neutral place to join, create, or switch communities. +

+ This is your community home. It stays available whenever you want a + neutral place to join, create, or switch communities.

) : null}