{"_id":"@alexkorban/html-bookshelf","_rev":"3-220fc1afc56a8e70e42874fcaf97d6a0","name":"@alexkorban/html-bookshelf","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@alexkorban/html-bookshelf","version":"0.1.0","keywords":["bookshelf","renderer"],"license":"MIT","_id":"@alexkorban/html-bookshelf@0.1.0","maintainers":[{"name":"alexkorban","email":"npm@korban.net"}],"homepage":"https://github.com/alexkorban/html-bookshelf#readme","bugs":{"url":"https://github.com/alexkorban/html-bookshelf/issues"},"bin":{"html-bookshelf":"dist/cli.js"},"dist":{"shasum":"ae2397fbf261c085192bb323190c5ac7a677299b","tarball":"https://registry.npmjs.org/@alexkorban/html-bookshelf/-/html-bookshelf-0.1.0.tgz","fileCount":7,"integrity":"sha512-cC5dMMtRlnpHULCR9MeU8TBmEuyyhARq6dKtJPnqbJuYFxjQ+WF94tRpcIp859VObkRCo4F8Imeu4EJcUcTTJg==","signatures":[{"sig":"MEQCIElSimsCyT3yfYWBJSeB/NKeajLHaHbkVd57IfIbJgDxAiAA16sFXh2ef9C8UQ3hEuDuF2AkdXQi8cCQF/6ga7c0xA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":273257},"main":"./dist/index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./dist/index.js","./styles.css":"./dist/styles.css"},"gitHead":"f237829b6ba49c8381583ffa047dc1d83f3166c3","scripts":{"test":"vitest run","build":"node scripts/build.js","prepack":"npm run build","typecheck":"tsc -p jsconfig.json","test:watch":"vitest"},"_npmUser":{"name":"alexkorban","email":"npm@korban.net"},"repository":{"url":"git+https://github.com/alexkorban/html-bookshelf.git","type":"git"},"_npmVersion":"11.5.2","description":"Render responsive HTML and CSS bookshelves from book collections.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"ramda":"^0.30.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26.1.0","vitest":"^4.1.11","esbuild":"^0.28.2","typescript":"^5.9.3","@types/node":"^26.4.0","@types/ramda":"^0.32.0","@playwright/test":"^1.56.1"},"_npmOperationalInternal":{"tmp":"tmp/html-bookshelf_0.1.0_1789209980695_0.8011526222037277","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alexkorban/html-bookshelf","version":"0.2.0","keywords":["bookshelf","renderer"],"license":"MIT","_id":"@alexkorban/html-bookshelf@0.2.0","maintainers":[{"name":"alexkorban","email":"npm@korban.net"}],"homepage":"https://github.com/alexkorban/html-bookshelf#readme","bugs":{"url":"https://github.com/alexkorban/html-bookshelf/issues"},"bin":{"html-bookshelf":"dist/cli.js"},"dist":{"shasum":"3b22cbaa1b640b6eabcae80872812758b8daa1aa","tarball":"https://registry.npmjs.org/@alexkorban/html-bookshelf/-/html-bookshelf-0.2.0.tgz","fileCount":7,"integrity":"sha512-Hi/P0hiAMiaYV7WocZ0h9gY0DuBptjAuAOtb2KAUgBw+3UGKAnBJMs2tcnntXRIVIOyVhZ4nI7Zzm56plS8gAA==","signatures":[{"sig":"MEQCIDqy3nX1eOYbfMFNki5FmvtVhTs8E4Iu26foxVNJvxQ+AiBWgQBeu0WHmM8vdiap0XRRZUa/LtfMApdE40QT5uP08g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDvrNRna00++whVv7xdIYZAMCG/qXX56h1ENxCdYb3UFQIgOS/HXXYXzDIKxyCKXh/jDsp/RGYWWG5KbFrgSeHoeNU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":287493},"main":"./dist/index.js","type":"module","engines":{"node":">=20"},"exports":{".":"./dist/index.js","./styles.css":"./dist/styles.css"},"gitHead":"d72d2f964a6cf1eb59063e5bcfb199de0f254be0","scripts":{"test":"vitest run","build":"node scripts/build.js","prepack":"npm run build","typecheck":"tsc -p jsconfig.json","test:watch":"vitest"},"_npmUser":{"name":"alexkorban","email":"npm@korban.net"},"repository":{"url":"git+https://github.com/alexkorban/html-bookshelf.git","type":"git"},"_npmVersion":"11.5.2","description":"Render responsive HTML and CSS bookshelves from book collections.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"ramda":"^0.30.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26.1.0","vitest":"^4.1.11","esbuild":"^0.28.2","typescript":"^5.9.3","@types/node":"^26.4.0","@types/ramda":"^0.32.0","@playwright/test":"^1.56.1"},"_npmOperationalInternal":{"tmp":"tmp/html-bookshelf_0.2.0_1789283996672_0.9095199747790266","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"_id":"@alexkorban/html-bookshelf@0.2.1","bin":{"html-bookshelf":"dist/cli.js"},"bugs":{"url":"https://github.com/alexkorban/html-bookshelf/issues"},"dist":{"shasum":"b14c692c2fcd1998179f1e13f46c4d4a0853c61d","tarball":"https://registry.npmjs.org/@alexkorban/html-bookshelf/-/html-bookshelf-0.2.1.tgz","fileCount":7,"integrity":"sha512-DVgjJb5RKz+C3TFE9RhGWO+gqBhpEbRFV1PBPvqxnUMrmw+l83oHFuo82aIOpdr/DgC1qkmo2FE5EStWRrYftw==","signatures":[{"sig":"MEYCIQDbsYz7gd5EdsWi1P4pRw8DpBX7onLLTaM6BScs/+xYVgIhALiCqRvFjc/6zx3/rj44/pcG8uoJ63Fk4nasVPkgR1bw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFrlWd8EQ6s5XFDwoX9EqVNFWfwKrieYrte9nX3uvHATAiEA123Pp4MhUN3fqG6DA/eM/rYkXi3u8kmwWxoki1YjyO4="}],"unpackedSize":289117},"main":"./dist/index.js","name":"@alexkorban/html-bookshelf","type":"module","engines":{"node":">=20"},"exports":{".":"./dist/index.js","./styles.css":"./dist/styles.css"},"gitHead":"23b9aba741e766803a825439994c69958e262857","license":"MIT","scripts":{"test":"vitest run","build":"node scripts/build.js","prepack":"npm run build","typecheck":"tsc -p jsconfig.json","test:watch":"vitest"},"version":"0.2.1","_npmUser":{"name":"alexkorban","email":"npm@korban.net"},"homepage":"https://github.com/alexkorban/html-bookshelf#readme","keywords":["bookshelf","renderer"],"repository":{"url":"git+https://github.com/alexkorban/html-bookshelf.git","type":"git"},"_npmVersion":"11.5.2","description":"Render responsive HTML and CSS bookshelves from book collections.","directories":{},"maintainers":[{"name":"alexkorban","email":"npm@korban.net"}],"_nodeVersion":"22.16.0","dependencies":{"ramda":"^0.30.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26.1.0","vitest":"^4.1.11","esbuild":"^0.28.2","typescript":"^5.9.3","@types/node":"^26.4.0","@types/ramda":"^0.32.0","@playwright/test":"^1.56.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/html-bookshelf_0.2.1_1789815816336_0.3340599258819281"}}},"time":{"created":"2026-09-12T10:46:20.372Z","modified":"2026-09-19T11:03:36.564Z","0.1.0":"2026-09-12T10:46:20.832Z","0.2.0":"2026-09-13T07:19:56.772Z","0.2.1":"2026-09-19T11:03:36.421Z"},"bugs":{"url":"https://github.com/alexkorban/html-bookshelf/issues"},"license":"MIT","homepage":"https://github.com/alexkorban/html-bookshelf#readme","keywords":["bookshelf","renderer"],"repository":{"url":"git+https://github.com/alexkorban/html-bookshelf.git","type":"git"},"description":"Render responsive HTML and CSS bookshelves from book collections.","maintainers":[{"name":"alexkorban","email":"npm@korban.net"}],"readme":"# @alexkorban/html-bookshelf\n\nRender an ordered book collection as an accessible, responsive HTML and CSS\nbookshelf.\n\n```js\nimport { bookshelfFragment } from '@alexkorban/html-bookshelf'\n\nconst [ok, fragment] = bookshelfFragment([\n  { title: 'Diaspora', author: 'Greg Egan', pages: 412, url: '/books/dune' }\n], { theme: 'modern', layout: 'auto', label: 'Reading list' })\n```\n\n`bookshelfFragment` returns `[true, html]` on success, or a plain\n`[false, validationError]` tuple. `mountBookshelf(target, books, options)`\nuses the same pure renderer and replaces a target HTML element only after\nvalidation succeeds.\n\nFor hosts that load the stylesheet themselves, use `style: 'none'` and import:\n\n```js\nimport '@alexkorban/html-bookshelf/styles.css'\n```\n\nThe renderer still emits a small inline per-instance container query for an\narbitrary Stack Layout threshold.\n\n## Shelf widths\n\nShelf Layout uses one page-count scale across all rows: 2rem per 200 pages,\nsubject to the minimum and maximum spine widths. Books keep their calculated\nwidth when they wrap, and unused space remains at the end of each row.\nThe shelf support still spans the full container width.\n\nThe default minimum spine width is 2.5rem. The shared maximum is the smallest\nof 7rem, 22% of the container width, and 42% of the shortest permitted spine\nheight; a larger minimum takes precedence. These limits keep text readable\nand book proportions bounded, but also limit page-count accuracy at the\nextremes. Stack Layout continues to use the available container width.\n\n## Mounting from an HTML list\n\nUse `mountBookshelfFromList(listElement, options)` to replace an existing `ol`\nor `ul` with a Bookshelf. The list stays readable, with working links, when\nJavaScript is disabled or fails to load.\n\n```html\n<ol id=\"reading-list\">\n  <li data-hbs-pages=\"412\">\n    <a data-hbs-title href=\"/books/dune\">Dune</a>\n    by <span data-hbs-author>Frank Herbert</span>\n  </li>\n  <li>\n    <span data-hbs-title>Kindred</span>\n    by <span data-hbs-author>Octavia E. Butler</span>\n  </li>\n</ol>\n```\n\nRun the mount after the list is available in the DOM:\n\n```js\nimport { mountBookshelfFromList } from '@alexkorban/html-bookshelf'\n\nconst result = mountBookshelfFromList(\n  document.querySelector('#reading-list'),\n  { theme: 'modern', label: 'Reading list' }\n)\n```\n\nThe browser script also exports `HtmlBookshelf.mountBookshelfFromList`.\n\n- The target must be an HTML `ol` or `ul` with a parent node. It is replaced\n  in place; its attributes, including `id` and `class`, are not copied.\n- Direct child `li` elements supply Book Entries in DOM order. The collection\n  must be non-empty, and nested lists are not supported. Other direct children do not supply entries.\n- Each item must contain exactly one `[data-hbs-title]` and one\n  `[data-hbs-author]` element. Their trimmed `textContent` supplies literal\n  Book Text; inline formatting is not retained.\n- Optional `data-hbs-pages` must contain a positive decimal integer. Surrounding whitespace is allowed. Omit it to use the default of 200 pages.\n- The first `a[href]` within each item supplies its Destination URL, including when the link is outside the title. Relative URLs remain relative. If no anchor has an `href`, the book has no link. An empty or whitespace-only\n  `href` is invalid. Other data attributes are ignored.\n- All existing rendering options apply. Either source list type produces an\n  ordered Bookshelf collection.\n\nThe function returns `[true, null]` on success, or `[false, error]` on failure.\n\nAn invalid target returns `target-error`; missing or duplicate field markers\nand nested lists return `source-error` with indexed issue paths such as\n`[1].title`. Invalid Book Entry values, an empty collection, or invalid options return `validation-error`. Extraction and validation finish before the source is replaced or styles are installed, so failures leave the original nodes intact.\n\nThis is a one-time conversion. Calling it again with the replaced source\nreturns a `target-error` because that list no longer has a parent. No initial\nhiding rule or separate `<noscript>` content is needed.\n\n## Command line\n\n```sh\nnpx @alexkorban/html-bookshelf books.json --theme mixed \\\n  --font Aptos --font Merriweather \\\n  --label \"2026 reading list\" --document --output bookshelf.html\n```\n\nUse `-` instead of `books.json` to read JSON from standard input. Invalid\ninput is written to standard error and returns an unsuccessful exit status.\n\n## Development\n\nNode.js 20 or later is required.\n\n```sh\nnpm install\nnpm test\nnpm run typecheck\nnpm run build\n```\n","readmeFilename":"README.md"}