From 73f6f75559275bf624c2071e9122ffbbb28fc6a9 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:01 -0300 Subject: [PATCH 01/18] feat(bento-grid): add BentoGrid and BentoGridItem component implementation --- src/dashkit/BentoGrid/BentoGrid.tsx | 388 ++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 src/dashkit/BentoGrid/BentoGrid.tsx diff --git a/src/dashkit/BentoGrid/BentoGrid.tsx b/src/dashkit/BentoGrid/BentoGrid.tsx new file mode 100644 index 0000000..106846c --- /dev/null +++ b/src/dashkit/BentoGrid/BentoGrid.tsx @@ -0,0 +1,388 @@ +import * as React from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { cn } from '../utils/cn'; + +export type BentoBreakpoint = 'base' | 'sm' | 'md' | 'lg' | 'xl'; +export type BentoResponsiveValue = T | Partial>; + +export type BentoCols = 1 | 2 | 3 | 4 | 5 | 6 | 12; +export type BentoColSpan = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 'full'; +export type BentoRowSpan = 1 | 2 | 3 | 4 | 5 | 6; +export type BentoGap = 'none' | 'sm' | 'md' | 'lg'; +export type BentoRounded = 'none' | 'sm' | 'md' | 'lg' | 'xl'; +export type BentoSurface = 'card' | 'background' | 'muted' | 'transparent'; +export type BentoPadding = 'none' | 'sm' | 'md' | 'lg'; + +const COLS_MAP: Record> = { + base: { + 1: 'grid-cols-1', + 2: 'grid-cols-2', + 3: 'grid-cols-3', + 4: 'grid-cols-4', + 5: 'grid-cols-5', + 6: 'grid-cols-6', + 12: 'grid-cols-12', + }, + sm: { + 1: 'sm:grid-cols-1', + 2: 'sm:grid-cols-2', + 3: 'sm:grid-cols-3', + 4: 'sm:grid-cols-4', + 5: 'sm:grid-cols-5', + 6: 'sm:grid-cols-6', + 12: 'sm:grid-cols-12', + }, + md: { + 1: 'md:grid-cols-1', + 2: 'md:grid-cols-2', + 3: 'md:grid-cols-3', + 4: 'md:grid-cols-4', + 5: 'md:grid-cols-5', + 6: 'md:grid-cols-6', + 12: 'md:grid-cols-12', + }, + lg: { + 1: 'lg:grid-cols-1', + 2: 'lg:grid-cols-2', + 3: 'lg:grid-cols-3', + 4: 'lg:grid-cols-4', + 5: 'lg:grid-cols-5', + 6: 'lg:grid-cols-6', + 12: 'lg:grid-cols-12', + }, + xl: { + 1: 'xl:grid-cols-1', + 2: 'xl:grid-cols-2', + 3: 'xl:grid-cols-3', + 4: 'xl:grid-cols-4', + 5: 'xl:grid-cols-5', + 6: 'xl:grid-cols-6', + 12: 'xl:grid-cols-12', + }, +}; + +const COL_SPAN_MAP: Record> = { + base: { + 1: 'col-span-1', + 2: 'col-span-2', + 3: 'col-span-3', + 4: 'col-span-4', + 5: 'col-span-5', + 6: 'col-span-6', + 7: 'col-span-7', + 8: 'col-span-8', + 9: 'col-span-9', + 10: 'col-span-10', + 11: 'col-span-11', + 12: 'col-span-12', + full: 'col-span-full', + }, + sm: { + 1: 'sm:col-span-1', + 2: 'sm:col-span-2', + 3: 'sm:col-span-3', + 4: 'sm:col-span-4', + 5: 'sm:col-span-5', + 6: 'sm:col-span-6', + 7: 'sm:col-span-7', + 8: 'sm:col-span-8', + 9: 'sm:col-span-9', + 10: 'sm:col-span-10', + 11: 'sm:col-span-11', + 12: 'sm:col-span-12', + full: 'sm:col-span-full', + }, + md: { + 1: 'md:col-span-1', + 2: 'md:col-span-2', + 3: 'md:col-span-3', + 4: 'md:col-span-4', + 5: 'md:col-span-5', + 6: 'md:col-span-6', + 7: 'md:col-span-7', + 8: 'md:col-span-8', + 9: 'md:col-span-9', + 10: 'md:col-span-10', + 11: 'md:col-span-11', + 12: 'md:col-span-12', + full: 'md:col-span-full', + }, + lg: { + 1: 'lg:col-span-1', + 2: 'lg:col-span-2', + 3: 'lg:col-span-3', + 4: 'lg:col-span-4', + 5: 'lg:col-span-5', + 6: 'lg:col-span-6', + 7: 'lg:col-span-7', + 8: 'lg:col-span-8', + 9: 'lg:col-span-9', + 10: 'lg:col-span-10', + 11: 'lg:col-span-11', + 12: 'lg:col-span-12', + full: 'lg:col-span-full', + }, + xl: { + 1: 'xl:col-span-1', + 2: 'xl:col-span-2', + 3: 'xl:col-span-3', + 4: 'xl:col-span-4', + 5: 'xl:col-span-5', + 6: 'xl:col-span-6', + 7: 'xl:col-span-7', + 8: 'xl:col-span-8', + 9: 'xl:col-span-9', + 10: 'xl:col-span-10', + 11: 'xl:col-span-11', + 12: 'xl:col-span-12', + full: 'xl:col-span-full', + }, +}; + +const ROW_SPAN_MAP: Record> = { + base: { + 1: 'row-span-1', + 2: 'row-span-2', + 3: 'row-span-3', + 4: 'row-span-4', + 5: 'row-span-5', + 6: 'row-span-6', + }, + sm: { + 1: 'sm:row-span-1', + 2: 'sm:row-span-2', + 3: 'sm:row-span-3', + 4: 'sm:row-span-4', + 5: 'sm:row-span-5', + 6: 'sm:row-span-6', + }, + md: { + 1: 'md:row-span-1', + 2: 'md:row-span-2', + 3: 'md:row-span-3', + 4: 'md:row-span-4', + 5: 'md:row-span-5', + 6: 'md:row-span-6', + }, + lg: { + 1: 'lg:row-span-1', + 2: 'lg:row-span-2', + 3: 'lg:row-span-3', + 4: 'lg:row-span-4', + 5: 'lg:row-span-5', + 6: 'lg:row-span-6', + }, + xl: { + 1: 'xl:row-span-1', + 2: 'xl:row-span-2', + 3: 'xl:row-span-3', + 4: 'xl:row-span-4', + 5: 'xl:row-span-5', + 6: 'xl:row-span-6', + }, +}; + +const BREAKPOINT_ORDER: BentoBreakpoint[] = ['base', 'sm', 'md', 'lg', 'xl']; + +function resolveColsClasses(cols: BentoResponsiveValue): string { + if (typeof cols === 'number') { + if (cols === 1) return COLS_MAP.base[1]; + return `${COLS_MAP.base[1]} ${COLS_MAP.md[cols]}`; + } + + return BREAKPOINT_ORDER.map((bp) => { + const val = cols[bp]; + return val !== undefined ? COLS_MAP[bp][val] : ''; + }) + .filter(Boolean) + .join(' '); +} + +function resolveColSpanClasses(colSpan: BentoResponsiveValue): string { + if (typeof colSpan === 'number' || colSpan === 'full') { + if (colSpan === 1) return COL_SPAN_MAP.base[1]; + if (colSpan === 'full') return COL_SPAN_MAP.base.full; + return `${COL_SPAN_MAP.base.full} ${COL_SPAN_MAP.md[colSpan]}`; + } + + return BREAKPOINT_ORDER.map((bp) => { + const val = colSpan[bp]; + return val !== undefined ? COL_SPAN_MAP[bp][val] : ''; + }) + .filter(Boolean) + .join(' '); +} + +function resolveRowSpanClasses(rowSpan: BentoResponsiveValue): string { + if (typeof rowSpan === 'number') { + if (rowSpan === 1) return ROW_SPAN_MAP.base[1]; + return `${ROW_SPAN_MAP.base[1]} ${ROW_SPAN_MAP.md[rowSpan]}`; + } + + return BREAKPOINT_ORDER.map((bp) => { + const val = rowSpan[bp]; + return val !== undefined ? ROW_SPAN_MAP[bp][val] : ''; + }) + .filter(Boolean) + .join(' '); +} + +const bentoGridVariants = cva('grid w-full font-sans', { + variants: { + gap: { + none: 'gap-px bg-border overflow-hidden', + sm: 'gap-2', + md: 'gap-4', + lg: 'gap-6', + }, + bordered: { + true: '', + false: 'border-0', + }, + rounded: { + none: 'rounded-none', + sm: 'rounded-sm', + md: 'rounded-md', + lg: 'rounded-lg', + xl: 'rounded-xl', + }, + }, + compoundVariants: [ + { + gap: 'none', + bordered: true, + className: 'border border-border', + }, + ], + defaultVariants: { + gap: 'none', + bordered: true, + rounded: 'xl', + }, +}); + +const bentoGridItemVariants = cva( + 'relative flex flex-col min-w-0 text-card-foreground transition-colors', + { + variants: { + surface: { + card: 'bg-card text-card-foreground', + background: 'bg-background text-foreground', + muted: 'bg-muted/60 dark:bg-muted/40 text-foreground', + transparent: 'bg-transparent text-foreground', + }, + padding: { + none: 'p-0', + sm: 'p-4', + md: 'p-6', + lg: 'p-8', + }, + divided: { + true: 'ring-1 ring-border', + false: 'border border-border rounded-xl', + }, + }, + defaultVariants: { + surface: 'card', + padding: 'md', + divided: true, + }, + } +); + +interface BentoGridContextValue { + divided: boolean; +} + +const BentoGridContext = React.createContext({ + divided: true, +}); + +export interface BentoGridProps + extends React.HTMLAttributes, + VariantProps { + cols?: BentoResponsiveValue; + autoRows?: string; +} + +export const BentoGrid = React.forwardRef( + function BentoGrid( + { + cols = 3, + gap = 'none', + bordered = true, + rounded = 'xl', + autoRows, + style, + className, + children, + ...props + }, + ref + ) { + const divided = (gap ?? 'none') === 'none'; + const mergedStyle = autoRows ? { gridAutoRows: autoRows, ...style } : style; + + return ( + +
+ {children} +
+
+ ); + } +); + +BentoGrid.displayName = 'BentoGrid'; + +export interface BentoGridItemProps + extends React.HTMLAttributes, + Omit, 'divided'> { + colSpan?: BentoResponsiveValue; + rowSpan?: BentoResponsiveValue; +} + +export const BentoGridItem = React.forwardRef( + function BentoGridItem( + { + colSpan = 1, + rowSpan = 1, + surface = 'card', + padding = 'md', + className, + children, + ...props + }, + ref + ) { + const { divided } = React.useContext(BentoGridContext); + + return ( +
+ {children} +
+ ); + } +); + +BentoGridItem.displayName = 'BentoGridItem'; From b421ac1e07142afebf2e6cafe657a4e012befa65 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:02 -0300 Subject: [PATCH 02/18] feat(bento-grid): add barrel export for BentoGrid --- src/dashkit/BentoGrid/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/dashkit/BentoGrid/index.ts diff --git a/src/dashkit/BentoGrid/index.ts b/src/dashkit/BentoGrid/index.ts new file mode 100644 index 0000000..cb5f621 --- /dev/null +++ b/src/dashkit/BentoGrid/index.ts @@ -0,0 +1 @@ +export * from './BentoGrid'; From 007a5d5e55be82736dc1ccf46390545eff0b8496 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:03 -0300 Subject: [PATCH 03/18] test(bento-grid): add unit tests for BentoGrid and BentoGridItem --- src/dashkit/BentoGrid/BentoGrid.test.tsx | 109 +++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/dashkit/BentoGrid/BentoGrid.test.tsx diff --git a/src/dashkit/BentoGrid/BentoGrid.test.tsx b/src/dashkit/BentoGrid/BentoGrid.test.tsx new file mode 100644 index 0000000..5a01504 --- /dev/null +++ b/src/dashkit/BentoGrid/BentoGrid.test.tsx @@ -0,0 +1,109 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import { BentoGrid, BentoGridItem } from './index'; + +describe('BentoGrid', () => { + it('renders grid and items with default hairline border coherence classes', () => { + render( + + Cell 1 + Cell 2 + + ); + + const grid = screen.getByTestId('grid'); + const cell1 = screen.getByText('Cell 1'); + + expect(grid).toHaveAttribute('data-slot', 'bento-grid'); + expect(grid).toHaveClass('grid', 'gap-px', 'bg-border', 'border', 'border-border', 'rounded-xl'); + expect(grid).toHaveClass('grid-cols-1', 'md:grid-cols-3'); + + expect(cell1).toHaveAttribute('data-slot', 'bento-grid-item'); + expect(cell1).toHaveClass('bg-card', 'p-6', 'ring-1', 'ring-border', 'col-span-1', 'row-span-1'); + }); + + it('supports numeric cols, colSpan, and rowSpan with mobile-first responsive defaults', () => { + render( + + + Irregular Cell + + + Footer Cell + + + ); + + const grid = screen.getByTestId('grid'); + const irregular = screen.getByText('Irregular Cell'); + const footer = screen.getByText('Footer Cell'); + + expect(grid).toHaveClass('grid-cols-1', 'md:grid-cols-12'); + expect(irregular).toHaveClass('col-span-full', 'md:col-span-4', 'row-span-1', 'md:row-span-2'); + expect(footer).toHaveClass('col-span-full'); + }); + + it('supports responsive breakpoint objects for cols, colSpan, and rowSpan', () => { + render( + + + Responsive Cell + + + ); + + const grid = screen.getByTestId('grid'); + const cell = screen.getByText('Responsive Cell'); + + expect(grid).toHaveClass('grid-cols-1', 'sm:grid-cols-2', 'md:grid-cols-4', 'lg:grid-cols-12'); + expect(cell).toHaveClass( + 'col-span-1', + 'sm:col-span-2', + 'md:col-span-2', + 'lg:col-span-5', + 'row-span-1', + 'lg:row-span-2' + ); + }); + + it('applies rounded, bordered, gap, and autoRows props correctly', () => { + render( + + + Spaced Cell + + + ); + + const grid = screen.getByTestId('grid'); + const cell = screen.getByText('Spaced Cell'); + + expect(grid).toHaveClass('rounded-none', 'border-0', 'gap-4'); + expect(grid).toHaveStyle({ gridAutoRows: 'minmax(180px, auto)' }); + expect(cell).toHaveClass('bg-muted/60', 'p-4', 'border', 'border-border', 'rounded-xl'); + }); + + it('forwards refs to BentoGrid and BentoGridItem', () => { + const gridRef = React.createRef(); + const itemRef = React.createRef(); + + render( + + Ref Cell + + ); + + expect(gridRef.current).toBeInstanceOf(HTMLDivElement); + expect(itemRef.current).toBeInstanceOf(HTMLDivElement); + }); +}); From 41f146b633237f4198265aa9c5083f7171f60304 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:03 -0300 Subject: [PATCH 04/18] feat(dashkit): export BentoGrid in root barrel --- src/dashkit/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dashkit/index.ts b/src/dashkit/index.ts index 9fb647f..f78f98a 100644 --- a/src/dashkit/index.ts +++ b/src/dashkit/index.ts @@ -5,6 +5,7 @@ export * from './Avatar'; export * from './Backdrop'; export * from './Badge'; export * from './BarChart'; +export * from './BentoGrid'; export * from './Breadcrumb'; export * from './Button'; export * from './ButtonGroup'; From 26a275ec99daf16d2a9a5e6ee078fdbb54cacb75 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:04 -0300 Subject: [PATCH 05/18] feat(registry): register bento-grid component for CLI --- scripts/registry.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/registry.ts b/scripts/registry.ts index 98ab12b..8fe47f7 100644 --- a/scripts/registry.ts +++ b/scripts/registry.ts @@ -45,6 +45,15 @@ export const registry: Record = { dependencies: ['clsx', 'tailwind-merge'], registryDependencies: ['badge'], }, + 'bento-grid': { + name: 'BentoGrid', + files: [ + 'src/dashkit/BentoGrid/BentoGrid.tsx', + 'src/dashkit/BentoGrid/BentoGrid.test.tsx', + 'src/dashkit/BentoGrid/index.ts' + ], + dependencies: ['clsx', 'tailwind-merge', 'class-variance-authority'], + }, button: { name: 'Button', files: [ From 15e36ebf8f887b103b54c60900e538fe8d1c88bd Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:05 -0300 Subject: [PATCH 06/18] docs(llms): add BentoGrid to Layout & Containers category --- scripts/generate-llms.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/generate-llms.ts b/scripts/generate-llms.ts index ddd1853..ac959ed 100644 --- a/scripts/generate-llms.ts +++ b/scripts/generate-llms.ts @@ -20,6 +20,7 @@ export const CATEGORIES: ComponentCategory[] = [ name: 'Layout & Containers', components: [ { name: 'Accordion', slug: 'accordion', mdxFile: 'accordion.mdx' }, + { name: 'BentoGrid', slug: 'bento-grid', mdxFile: 'bento-grid.mdx' }, { name: 'Card', slug: 'card', mdxFile: 'card.mdx' }, { name: 'Surface', slug: 'surface', mdxFile: 'surface.mdx' }, { name: 'Divider', slug: 'divider', mdxFile: 'divider.mdx' }, From 1e7d9618b73ad91d507c024c6829eaa24515fd2e Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:06 -0300 Subject: [PATCH 07/18] test(llms): update expected catalog component count to 56 --- scripts/tests/llms.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/tests/llms.test.ts b/scripts/tests/llms.test.ts index db314f8..76af71a 100644 --- a/scripts/tests/llms.test.ts +++ b/scripts/tests/llms.test.ts @@ -22,7 +22,7 @@ describe('LLMs Documentation Generator', () => { expect(llmsContent.length).toBeGreaterThan(1000); expect(llmsFullContent.length).toBeGreaterThan(10000); - expect(registryJson.totalComponents).toBeGreaterThanOrEqual(55); + expect(registryJson.totalComponents).toBeGreaterThanOrEqual(56); expect(registryJson.components.button.name).toBe('Button'); expect(Array.isArray(registryJson.components.button.exports)).toBe(true); }); @@ -57,7 +57,7 @@ describe('LLMs Documentation Generator', () => { } } - expect(totalCatalogComponents).toBe(55); + expect(totalCatalogComponents).toBe(56); }); it('should resolve every catalog link anchor in llms.txt to an existing heading in llms-full.txt', () => { @@ -70,7 +70,7 @@ describe('LLMs Documentation Generator', () => { } const linkAnchors = [...llmsContent.matchAll(/\(llms-full\.txt#([^)]+)\)/g)].map((m) => m[1]); - expect(linkAnchors.length).toBe(55); + expect(linkAnchors.length).toBe(56); for (const anchor of linkAnchors) { expect(fullHeadingAnchors.has(anchor), `Broken anchor link: #${anchor}`).toBe(true); From 012e0fa6f41b38ecf4838ebdda128e4064e60452 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:06 -0300 Subject: [PATCH 08/18] docs(bento-grid): add interactive MDX documentation page --- src/pages/bento-grid.mdx | 239 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 src/pages/bento-grid.mdx diff --git a/src/pages/bento-grid.mdx b/src/pages/bento-grid.mdx new file mode 100644 index 0000000..57eabd1 --- /dev/null +++ b/src/pages/bento-grid.mdx @@ -0,0 +1,239 @@ +import { BentoGrid, BentoGridItem } from '../dashkit/BentoGrid'; +import { Badge } from '../dashkit/Badge'; + +# Bento Grid + +A modular grid layout system for building regular and irregular Bento or Hairline grids while maintaining seamless `1px` shared border coherence across arbitrary column/row spans and responsive breakpoints. + +## Installation + +Add the BentoGrid component to your project using the Dashkit CLI: + +```bash +bunx carllosnc/dashkit add bento-grid +``` + +## Import + +```tsx +import { BentoGrid, BentoGridItem } from '@/dashkit/BentoGrid'; +``` + +## Irregular Hairline Grid + +By default, `BentoGrid` uses `gap="none"` to render a continuous `1px` hairline divider between cells without double borders—even when combining multi-row vertical spans (`rowSpan={2}`) and full-width spans (`colSpan="full"`). + +
+ + + 01 / Ingestion +
+

Edge Collector

+

Low-latency telemetry pipelines across 38 regions.

+
+
+ + + 02 / Cache +
+

99.98% Hit Rate

+

In-memory distributed read replicas.

+
+
+ + +
+ 03 / Architecture + +
+
+
1.42M req/s
+

+ Tall vertical cell spanning two rows alongside the 2x2 grid on the left, sharing a single 1px border on all sides. +

+
+ cluster-us-east-primary +
+ + + 04 / Storage +
+

Zero-Copy Snapshots

+

Instant branching for isolated preview environments.

+
+
+ + + 05 / Security +
+

mTLS Enforced

+

Automated certificate rotation every 24h.

+
+
+ + +
+ 06 / Full-Width Footer +

Global Consensus Layer

+

Spans all 12 columns across the bottom while preserving a crisp 1px top divider.

+
+ +
+
+
+ +```tsx + + Cell 1 + Cell 2 + + Cell 3 (Tall 2-Row Span) + + Cell 4 + Cell 5 + Cell 6 (Full Width) + +``` + +## Sharp Architectural Wireframe (`rounded="none"`) + +Set `rounded="none"` for a sharp editorial or architectural blueprint layout where every outer corner and inner intersection meets at 90 degrees. + +
+ + + col-span-4 + + + col-span-3 + + + col-span-5 / row-span-2 + + + col-span-4 + + + col-span-3 + + + col-span-full + + +
+ +```tsx + + col-span-4 + col-span-3 + + col-span-5 / row-span-2 + + col-span-4 + col-span-3 + col-span-full + +``` + +## Spaced Bento Cards + +Pass `gap="sm" | "md" | "lg"` when you want detached Bento cards instead of shared hairline borders. + +
+ + +

Detached Bento Card (2 cols)

+

Automatically switches to individual card borders when gap is enabled.

+
+ +

Side Card (1 col)

+

Muted surface variant.

+
+
+
+ +```tsx + + Detached Bento Card + Side Card + +``` + +## BentoGrid Props + +", + defaultValue: "3", + description: "Number of grid columns. Accepts a direct number (stacks on mobile, applies at md+) or a responsive breakpoint object ({ base, sm, md, lg, xl })." + }, + { + prop: "gap", + type: "'none' | 'sm' | 'md' | 'lg'", + defaultValue: "'none'", + description: "'none' enables the seamless 1px hairline border mode. Other values separate items into individual bordered cards." + }, + { + prop: "bordered", + type: "boolean", + defaultValue: "true", + description: "Whether to render the outer 1px perimeter border in hairline mode." + }, + { + prop: "rounded", + type: "'none' | 'sm' | 'md' | 'lg' | 'xl'", + defaultValue: "'xl'", + description: "Controls the outer corner radius of the grid container." + }, + { + prop: "autoRows", + type: "string", + defaultValue: "—", + description: "Sets the CSS grid-auto-rows property (e.g., 'minmax(140px, auto)')." + } + ]} +/> + +## BentoGridItem Props + +", + defaultValue: "1", + description: "Number of columns the cell spans. Accepts a direct value or a responsive breakpoint object." + }, + { + prop: "rowSpan", + type: "BentoResponsiveValue<1 | 2 | 3 | 4 | 5 | 6>", + defaultValue: "1", + description: "Number of rows the cell spans. Accepts a direct value or a responsive breakpoint object." + }, + { + prop: "surface", + type: "'card' | 'background' | 'muted' | 'transparent'", + defaultValue: "'card'", + description: "Semantic background surface color of the cell." + }, + { + prop: "padding", + type: "'none' | 'sm' | 'md' | 'lg'", + defaultValue: "'md'", + description: "Internal cell padding ('none' for full-bleed visuals, 'sm' = p-4, 'md' = p-6, 'lg' = p-8)." + } + ]} +/> From f61c0e4573128173002b0bf8efbea1725174bc53 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:07 -0300 Subject: [PATCH 09/18] feat(routes): add lazy route for BentoGridDocs --- src/routes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes.ts b/src/routes.ts index 2967c6f..f328c47 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -33,6 +33,7 @@ export const AccordionDocs = lazy(() => import('./pages/accordion.mdx')) export const ToastDocs = lazy(() => import('./pages/toast.mdx')) export const DropdownMenuDocs = lazy(() => import('./pages/dropdown-menu.mdx')) export const BadgeDocs = lazy(() => import('./pages/badge.mdx')) +export const BentoGridDocs = lazy(() => import('./pages/bento-grid.mdx')) export const CardDocs = lazy(() => import('./pages/card.mdx')) export const CliDocs = lazy(() => import('./pages/cli.mdx')) export const AiDocs = lazy(() => import('./pages/ai.mdx')) From d87748de7d4dc9cf6939e3ddf9103e75d63c863d Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:08 -0300 Subject: [PATCH 10/18] feat(router): register /docs/bento-grid route --- src/main.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.tsx b/src/main.tsx index 5780494..2923afb 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -22,6 +22,7 @@ import { BackdropDocs, BadgeDocs, BarChartDocs, + BentoGridDocs, BreadcrumbDocs, ButtonDocs, ButtonGroupDocs, @@ -113,6 +114,7 @@ createRoot(document.getElementById('root')!).render( } /> } /> } /> + } /> } /> } /> } /> From 3bea9ce0db049600251328aa103210769c9dddd9 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:08 -0300 Subject: [PATCH 11/18] docs(sidebar): add Bento Grid link to documentation sidebar --- src/layouts/DocsLayout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/DocsLayout.tsx b/src/layouts/DocsLayout.tsx index 724a283..1b69967 100644 --- a/src/layouts/DocsLayout.tsx +++ b/src/layouts/DocsLayout.tsx @@ -35,6 +35,7 @@ const navItems: SidebarSection[] = [ { to: '/docs/avatar', label: 'Avatar', icon: }, { to: '/docs/backdrop', label: 'Backdrop', icon: }, { to: '/docs/badge', label: 'Badge', icon: }, + { to: '/docs/bento-grid', label: 'Bento Grid', icon: }, { to: '/docs/breadcrumb', label: 'Breadcrumb', icon: }, { to: '/docs/button', label: 'Button', icon: }, { to: '/docs/button-group', label: 'Button Group', icon: }, From 010a71090a9e2ffaf83f16b8b872b5dbe9795599 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:09 -0300 Subject: [PATCH 12/18] docs(mdx): expose BentoGrid and BentoGridItem to MDX scope --- src/layouts/MdxWrapper.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/layouts/MdxWrapper.tsx b/src/layouts/MdxWrapper.tsx index c1bb1d1..a5d28c2 100644 --- a/src/layouts/MdxWrapper.tsx +++ b/src/layouts/MdxWrapper.tsx @@ -30,6 +30,7 @@ import { Sidebar, SidebarHeader, SidebarFooter, SidebarSection, SidebarItem } fr import { ImageExpander } from '../dashkit/ImageExpander/ImageExpander'; import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '../dashkit/Accordion'; import { Badge, FloatBadge } from '../dashkit/Badge'; +import { BentoGrid, BentoGridItem } from '../dashkit/BentoGrid'; import { Skeleton } from '../dashkit/Skeleton/Skeleton'; import { Spinner } from '../dashkit/Spinner/Spinner'; import { Navbar } from '../dashkit/Navbar'; @@ -238,6 +239,8 @@ const components: Record = { NavigationMenuLink, NavigationMenuViewport, StatsCard, + BentoGrid, + BentoGridItem, HiOutlineChartBarSquare, HiOutlineTicket, Preview: ({ children }: { children: ReactNode }) => ( From 4a773ab8456b5bff5428337eb4d621851059e95e Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:10 -0300 Subject: [PATCH 13/18] docs(components): add Bento Grid card to component overview grid --- src/partials/ComponentGrid.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/partials/ComponentGrid.tsx b/src/partials/ComponentGrid.tsx index 4f2940b..dcd9d8c 100644 --- a/src/partials/ComponentGrid.tsx +++ b/src/partials/ComponentGrid.tsx @@ -18,6 +18,7 @@ const components = [ { to: '/docs/backdrop', label: 'Backdrop', icon: , description: 'Dim the background for focus.' }, { to: '/docs/badge', label: 'Badge', icon: , description: 'Small status descriptors.' }, { to: '/docs/badge', label: 'Float Badge', icon: , description: 'Corner status indicators anchored to elements.' }, + { to: '/docs/bento-grid', label: 'Bento Grid', icon: , description: 'Regular and irregular hairline-bordered grids.' }, { to: '/charts/bar-chart', label: 'Bar Chart', icon: , description: 'Vertical or horizontal bar data.' }, { to: '/docs/breadcrumb', label: 'Breadcrumb', icon: , description: 'Navigation path indicators.' }, { to: '/docs/button', label: 'Button', icon: , description: 'Interactive action triggers.' }, From 97c93fe8c1ed0ce6352e33d923a89e4d6063b858 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 00:57:11 -0300 Subject: [PATCH 14/18] docs(examples): add Bento Grid showcase to AllComponentsExample --- src/pages/examples/AllComponentsExample.tsx | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/pages/examples/AllComponentsExample.tsx b/src/pages/examples/AllComponentsExample.tsx index e5338f1..be5e825 100644 --- a/src/pages/examples/AllComponentsExample.tsx +++ b/src/pages/examples/AllComponentsExample.tsx @@ -8,6 +8,7 @@ import { IconButton } from '../../dashkit/IconButton/IconButton'; import { Chip } from '../../dashkit/Chip/Chip'; import { Divider } from '../../dashkit/Divider/Divider'; import { Badge, FloatBadge } from '../../dashkit/Badge'; +import { BentoGrid, BentoGridItem } from '../../dashkit/BentoGrid'; import { AnimateNumber } from '../../dashkit/AnimateNumber'; import { CircularProgress } from '../../dashkit/CircularProgress/CircularProgress'; import { Avatar, AvatarGroup } from '../../dashkit/Avatar'; @@ -1644,6 +1645,50 @@ export const AllComponentsExample = () => { + + {/* BENTO GRID */} + + + Bento Grid + Regular and irregular hairline-bordered grids with responsive column and row spans. + + + + + 01 + Ingestion + + + 02 + Cache Layer + + + 03 / Tall Span +
+
99.99%
+

Spans 2 vertical rows with 1px border coherence.

+
+
+ + 04 + Zero-Copy Storage + + + 05 + mTLS Security + + + Global Consensus Footer (Full Span) + + +
+
+