{"id":51429512,"url":"https://github.com/bnomei/emdash-bento","last_synced_at":"2026-07-05T03:02:16.317Z","repository":{"id":365483989,"uuid":"1267750716","full_name":"bnomei/emdash-bento","owner":"bnomei","description":"Bento grid field widget for EmDash JSON fields, using Block Builder for nested blocks.","archived":false,"fork":false,"pushed_at":"2026-06-27T15:03:14.000Z","size":507,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T17:06:09.292Z","etag":null,"topics":["blocks","cms","columns","emdash","emdash-plugin","field-widget","grid","json","layout-builder","nested","react","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bnomei.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"bnomei","buy_me_a_coffee":"bnomei"}},"created_at":"2026-06-12T20:37:30.000Z","updated_at":"2026-06-18T21:02:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bnomei/emdash-bento","commit_stats":null,"previous_names":["bnomei/emdash-bento"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/bnomei/emdash-bento","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-bento","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-bento/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-bento/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-bento/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bnomei","download_url":"https://codeload.github.com/bnomei/emdash-bento/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnomei%2Femdash-bento/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35141966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blocks","cms","columns","emdash","emdash-plugin","field-widget","grid","json","layout-builder","nested","react","typescript"],"created_at":"2026-07-05T03:02:13.260Z","updated_at":"2026-07-05T03:02:16.243Z","avatar_url":"https://github.com/bnomei.png","language":"TypeScript","funding_links":["https://github.com/sponsors/bnomei","https://buymeacoffee.com/bnomei"],"categories":[],"sub_categories":[],"readme":"# @bnomei/emdash-bento\n\n[![npm version](https://img.shields.io/npm/v/@bnomei/emdash-bento.svg)](https://www.npmjs.com/package/@bnomei/emdash-bento)\n[![npm downloads](https://img.shields.io/npm/dm/@bnomei/emdash-bento.svg)](https://www.npmjs.com/package/@bnomei/emdash-bento)\n[![license](https://img.shields.io/npm/l/@bnomei/emdash-bento.svg)](https://www.npmjs.com/package/@bnomei/emdash-bento)\n[![types](https://img.shields.io/badge/types-included-blue.svg)](./package.json)\n[![source](https://img.shields.io/badge/source-GitHub-181717.svg?logo=github)](https://github.com/bnomei/emdash-bento)\n\nBento grid field widget for EmDash JSON fields.\n\n`@bnomei/emdash-bento` is a native EmDash plugin for row and\ncolumn layout data stored as plain JSON. It registers the `bento:layouts`\nadmin widget, reuses `@bnomei/emdash-blocks` for nested column content,\nand exports helpers for Astro renderers to prepare visible rows and translate\nfractional spans into a 12-column grid. Use it when editors need controlled\nlayout composition while templates still own the frontend markup.\n\n## What It Provides\n\n- Native EmDash plugin factory: `bentoPlugin()`.\n- JSON field widget: `bento:layouts`.\n- One editable layout pattern string per row.\n- Nested block controls through `options.blockDefinitions`.\n- Admin UI built with [Kumo UI](https://kumo-ui.com/) with full light and dark\n  mode support.\n- Frontend helpers: `visibleLayoutRows()` and `spanToGridColumns()`.\n\n## Install\n\n```sh\nnpm install @bnomei/emdash-bento @bnomei/emdash-blocks\n```\n\nRegister both plugins in `astro.config.mjs`:\n\n```js\nimport emdash from \"emdash/astro\";\nimport { bentoPlugin } from \"@bnomei/emdash-bento\";\nimport { blockBuilderPlugin } from \"@bnomei/emdash-blocks\";\n\nexport default {\n  integrations: [\n    emdash({\n      plugins: [blockBuilderPlugin(), bentoPlugin()],\n    }),\n  ],\n};\n```\n\n## Field Widget\n\nUse the widget on an EmDash `json` field:\n\n```json\n{\n  \"slug\": \"layouts\",\n  \"label\": \"Grid\",\n  \"type\": \"json\",\n  \"widget\": \"bento:layouts\",\n  \"options\": {\n    \"blockDefinitions\": [\n      {\n        \"type\": \"heading\",\n        \"label\": \"Heading\",\n        \"props\": [{ \"key\": \"text\", \"label\": \"Text\", \"type\": \"text\" }]\n      },\n      {\n        \"type\": \"text\",\n        \"label\": \"Text\",\n        \"props\": [{ \"key\": \"text\", \"label\": \"Text\", \"type\": \"markdown\" }]\n      },\n      {\n        \"type\": \"image\",\n        \"label\": \"Image\",\n        \"props\": [{ \"key\": \"image\", \"label\": \"Image\", \"type\": \"media\" }]\n      }\n    ]\n  }\n}\n```\n\n## Stored Value\n\n```ts\ntype LayoutBuilderValue = Array\u003c{\n  id: string;\n  layout: string;\n  settings?: Record\u003cstring, unknown\u003e;\n  columns: Array\u003c{\n    id: string;\n    span: string;\n    blocks: BlockBuilderValue;\n  }\u003e;\n}\u003e;\n```\n\nEmpty layout fields stay empty when the admin widget mounts, so opening an\nuntouched entry does not write a default row or mark the field dirty. The widget\nshows an empty state with the Add Layout button, and clicking that button creates\nthe first editable row.\n\nEach row stores exactly one `layout` pattern. The pattern is a comma-separated\nlist of column spans, for example `1/1`, `1/2, 1/2`, or `1/1, 1/3, 2/3`.\nEditing the pattern updates the row's columns by position, preserving existing\ncolumn blocks where a matching column index still exists. If the edited pattern\nhas fewer spans than the row already has columns, columns beyond that pattern\nare removed. The per-column `span` values mirror the row pattern and are stored\nso frontend renderers can read the same data directly.\n\nEditors can also append a column with the row-level Add Column button. New\ncolumns start as `1/1`, and the row pattern updates to match the visible\ncolumns.\n\nThe Add Layout button creates a new row with `1/1, 1/2, 1/3`; editors can\nchange that row's layout pattern after it has been added.\n\nEach column header also exposes a sorted width select and a remove button. The\nselect choices are the unique spans in that row's current layout pattern.\nLayouts can be reordered with up/down controls. Columns can be reordered with\nleft/right controls.\n\nThe admin widget renders columns on a 12-column CSS grid where `1/2` spans 6 grid columns and `1/3` spans 4 columns. If a row's widths add up to more than `1/1`, the extra columns wrap to the next line. The widget accepts comma-separated fractions with denominators up to twelfths, so editors can type one row pattern such as `1/1`, `1/2, 1/2`, or `1/1, 2/3, 1/3` and override it whenever the layout needs to change.\n\nNested blocks use `@bnomei/emdash-blocks` directly. Pass\n`options.blockDefinitions` to control the available nested block types and their\ntyped prop controls. Standalone block fields and layout column blocks share the\nsame definition shape.\n\n## Frontend Rendering\n\nRender layout rows as a 12-column CSS grid. The package exports helpers for the\ncommon data preparation and span calculation:\n\n```ts\nimport { layoutGridSpans, visibleLayoutRows } from \"@bnomei/emdash-bento\";\nimport { visibleBlocks } from \"@bnomei/emdash-blocks\";\n\nconst rows = visibleLayoutRows(entry.layouts);\n```\n\n```astro\n{\n  rows.map((row) =\u003e {\n    const gridSpans = layoutGridSpans(row.columns.map((column) =\u003e column.span));\n\n    return (\n      \u003csection class=\"layout\" data-layout-id={row.id}\u003e\n        {row.columns.map((column, columnIndex) =\u003e (\n          \u003cdiv\n            class=\"column\"\n            style={`--span: ${gridSpans[columnIndex] ?? 12}`}\n            data-column-id={column.id}\n          \u003e\n            {visibleBlocks(column.blocks).map((block) =\u003e renderBlock(block))}\n          \u003c/div\u003e\n        ))}\n      \u003c/section\u003e\n    );\n  })\n}\n```\n\n```css\n.layout {\n  display: grid;\n  grid-template-columns: repeat(12, minmax(0, 1fr));\n}\n\n.column {\n  grid-column: span var(--span) / span var(--span);\n}\n```\n\nThe helpers accept native Bento values. Migration from other systems should\nhappen before values are saved into EmDash.\n\n## Package Surface\n\n- ESM entry: `@bnomei/emdash-bento`.\n- Admin entry: `@bnomei/emdash-bento/admin`.\n- Type declarations are included from `dist/`.\n- Peer dependencies: `@bnomei/emdash-blocks` `^0.2.1`, `emdash`\n  `\u003e=0.17.0`, `react` `^18.0.0 || ^19.0.0`, `react-dom`\n  `^18.0.0 || ^19.0.0`, `@cloudflare/kumo` `^2.5.0`, and\n  `@phosphor-icons/react` `^2.1.10`.\n\n## Status\n\nThis package ships as a native EmDash plugin because the editor is a trusted React admin field widget. Package exports point at Viteplus `vp pack`-built `dist/` JavaScript and declarations; use `npm run build` for the production pack and `npm run pack:check` for the publint-verified pack check.\n\n## Related Packages\n\n- `@bnomei/emdash-blocks` provides the nested block editor used by Bento\n  columns.\n- `@bnomei/emdash-fields` provides smaller JSON-backed field widgets.\n\n## License\n\nMIT.\n\n## Screenshot\n\n![Bento screenshot](./screenshot.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnomei%2Femdash-bento","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbnomei%2Femdash-bento","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnomei%2Femdash-bento/lists"}