{"id":35170210,"url":"https://github.com/zkat/bookbook","last_synced_at":"2026-05-17T21:36:05.164Z","repository":{"id":304808507,"uuid":"1019990106","full_name":"zkat/bookbook","owner":"zkat","description":"scaffolded books app","archived":false,"fork":false,"pushed_at":"2025-07-16T04:37:30.000Z","size":146,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-31T08:41:27.905Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zkat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"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}},"created_at":"2025-07-15T07:14:17.000Z","updated_at":"2025-07-20T20:46:03.000Z","dependencies_parsed_at":"2025-07-15T22:48:54.674Z","dependency_job_id":"56191969-80b7-4d27-ac33-ba6f192a2ed7","html_url":"https://github.com/zkat/bookbook","commit_stats":null,"previous_names":["zkat/bookbook"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zkat/bookbook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fbookbook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fbookbook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fbookbook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fbookbook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zkat","download_url":"https://codeload.github.com/zkat/bookbook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fbookbook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33155852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T09:28:26.183Z","status":"ssl_error","status_checked_at":"2026-05-17T09:27:52.702Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2025-12-28T20:06:57.507Z","updated_at":"2026-05-17T21:36:05.159Z","avatar_url":"https://github.com/zkat.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bookbook\n\n## Table of Contents\n\n- [Features](#features)\n- [Setting up](#setting-up)\n  - [Prerequisites](#prerequisites)\n    - [Using ASDF for Runtimes](#using-asdf-for-runtimes)\n  - [Additional Tooling](#additional-tooling)\n  - [Up and Running](#up-and-running)\n- [Architecture](#architecture)\n\n## Features\n\n- User accounts, with authentication, login/signup, and email verification\n- User settings page scaffold\n- Primarily server-side rendered, with web components set up for if/when needed\n- Web components are ALSO SSR'd as needed\n- No Tailwind/React/etc. Just plain ol' HTML/CSS, a sprinkling of JS, and good vibes.\n- TypeScript support\n- Full setup for bundling and minifying JS and CSS from sources\n- Ready-to-use i18n and locale detection/configuration\n- Transactional email sending (needs a service for live site)\n- Rate limiting\n- Websockets available if needed (not connected by default)\n- Autoformatting for both JSTS and Elixir code\n- CSS-based light/dark/etc theming support with built-in picker\n- Scaffolded CSP\n- Ready-to-go DNS-based clustering for horizontal scaling (you don't need it)\n- Root template with a bunch of nice things:\n  - Mobile size-related meta tags\n  - Anti-AI no-scan stuff (on top of included robots.txt). For all the good it'll do :/\n  - OpenGraph/\"Twitter\" card properties, configurable per-page.\n\n## Setting up\n\n### Prerequisites\n\n\u003e [!TIP]\n\u003e See [Using ASDF for Runtimes](#using-asdf-for-runtimes) for a convenient way to handle the first two items below.\n\n- [Elixir](https://elixir-lang.org/install.html) and Erlang (Typically auto-installs with Elixir)\n- [NodeJS](https://nodejs.org/en/download/)\n- [Postgresql v13 or later](https://wiki.postgresql.org/wiki/Detailed_installation_guides) ATM the tests require a postgres/postgres user/password to run. Check [this doc](https://academind.com/tutorials/postgresql-start-stop-uninstall-upgrade-server#resetting-the-root-user-password) for info on how to reset your postgres password.\n  - A [docker-compose](./docker-compose.yaml) file is provided that will run\n    postgres configured appropriately\n- `inotify-tools` (Linux only) - Install through your preferred package manager\n  - [Check versions here](https://github.com/zkat/bookbook/blob/main/.tool-versions)\n\n\u003e [!NOTE]\n\u003e You can check [`.tool-versions`](https://github.com/zkat/bookbook/blob/main/.tool-versions) in this repository to see what this project is typically developed against.\n\n\u003e [!IMPORTANT]\n\u003e If `postgresql` installed via `homebrew`, make sure to run `/usr/local/opt/postgres/bin/createuser -s postgres`.\n\n#### Using ASDF for runtimes\n\nYou can use [ASDF](https://asdf-vm.com/) to install Elixir, Erlang, and NodejS\nat the correct versions (ensure [the necessary build\ndeps](https://github.com/asdf-vm/asdf-erlang?tab=readme-ov-file#before-asdf-install)\nare installed first):\n\n```shell\nasdf plugin add nodejs\nasdf plugin add erlang\nasdf plugin add elixir\nKERL_BUILD_DOCS=yes asdf install # will install erlang (w/ docs), elixir, and nodejs\n```\n\n### Additional Tooling\n\nIf you're using VS Code, you may find these extensions useful:\n\n- [ElixirLS extension](https://marketplace.visualstudio.com/items?itemName=JakeBecker.elixir-ls)\n- [lit-plugin extension](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin)\n- [gettext extension](https://marketplace.visualstudio.com/items?itemName=mrorz.language-gettext)\n- [HTML CSS Support](https://marketplace.visualstudio.com/items?itemName=ecmel.vscode-html-css)\n- [Dprint Code Formatter](https://marketplace.visualstudio.com/items?itemName=dprint.dprint)\n\n### Up and Running\n\nJust a couple more commands and we're all set:\n\n- `mix local.hex` to install hex package manager\n- `mix archive.install hex phx_new`\n- If using docker postgres: `docker compose up -d`\n- `mix setup` to install deps, create the database, install JS deps, and build JS assets\n- `mix phx.server` to run the server\n\nNow you can visit [`localhost:4000`](http://localhost:4000) from your browser.\n\n## Architecture\n\n### Stack\n\n#### Backend\n\n- [Elixir](https://elixir-lang.org/)\n- [PostgreSQL](https://www.postgresql.org/)\n- [Phoenix](https://www.phoenixframework.org/) (no LiveView)\n\n#### Frontend\n\n- Plain old HTML + CSS (only preprocessing for CSS is bundling/minification)\n- [Lit](https://lit.dev/) for dynamic components\n- [TypeScript](https://www.typescriptlang.org/)\n\n#### JS Considerations and SSR\n\nWe use TypeScript for all client-side code, and Lit for all client-side\ncomponents. Components should be small and focused, preferring to use\nserver-side Phoenix Components whenever possible.\n\nWe generate as much html/css as possible server-side, and try to keep our JS\npayloads as small as possible. Server-side rendering, or SSR, is done in three\nlayers:\n\n1. `Phoenix` controllers render a dynamic page with as much of the content in\n   regular light DOM as possible. This content can be styled with our global\n   CSS styles, and does not require JS to render/function.\n   - This uses [`Phoenix HEEx templates and\nComponents`](https://hexdocs.pm/phoenix/components.html).\n   - Within these templates, we can insert `Lit` components for things that\n     will absolutely need dynamic, client-side/JS behavior or will otherwise\n     have to do something special to function while offline.\n2. Once `Phoenix` renders these templates, they're passed through the\n   [`Plug`](https://hexdocs.pm/plug/readme.html) system, which eventually\n   invokes a `Lit` server-side renderer.\n   - This renderer takes the template, loads all existing components, and\n     pre-renders `Lit` components as far as server-side work will allow.\n   - In components, this behavior can be controlled with\n     [`isServer`](https://lit.dev/docs/api/misc/#isServer).\n3. Finally, all this server-side-rendered content is sent to the client, and\n   `Lit` components will be \"hydrated\" after all the other content and JS is\n   loaded.\n\nAs a general rule, we operate on \"the less JavaScript, the better\".\nDependencies should be few and far between, preferring to use built-in browser\nfeatures whenever possible. If a dependency is needed, it should be small and\nfocused, and not introduce a lot of overhead. Obviously, some things in\noffline apps are just going to bring in some bulk and that's ok, but whenever\nwe have a choice between two things, code size should be a significant\nconsideration in their evaluation.\n\nTo minimize JavasScript, we should err on the side of having components be\n`Phoenix`-based, and only use `Lit` components when absolutely necessary: even\nif they're server-side rendered, their JavaScript definitions still needs to\nload/hydrate, and are still shipped as part of our `.js` bundles.\n\nTo see what kind of weight a dependency brings in, you can use\n[BundlePhobia](https://bundlephobia.com).\n\n### Folder Structure\n\n- `assets/` - Frontend code\n  - `css/` - CSS files\n    - `components/` - CSS for (usually server-side) components\n    - `app.css` - main css entrypoint\n    - `theme-*.css` - variables for themes\n    - `variables.css` - non-theme-specific variables\n      - NOTE: While regular styles don't leak into shadow DOM, `--variables`\n        do, so we can use this for things we need to have consistent styling\n        for.\n  - `js/` - JS/TS files\n    - `components/` - Toplevel lit components\n    - `app.ts` - entry point for the overall site JS\n    - `lit-ssr.ts` - lit server-side renderer tool. Used as a server-side script. Kept here for convenience.\n    - Other files are pulled in by one of these.\n- `config/` - Configuration files\n  - Different configs for dev, prod, and test envs.\n  - `config.exs` - common config\n  - `runtime.exs` - more prod config\n- `lib/` - Elixir source code\n  - `bookbook/` - core business logic. No web stuff here. All modules are prefixed with `Bookbook`.\n  - `bookbook_web/` - all web stuff here. Modules are prefixed with\n    `BookbookWeb`. Uses `Bookbook` for any business logic.\n- `priv/` - miscellaneous things\n  - `gettext/` - this is where our (server-side) i18n stuff lives\n  - `repo/` - migrations, seeds, etc\n  - `static/` - static files. JS and CSS are compiled into here (but .gitignored)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkat%2Fbookbook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzkat%2Fbookbook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkat%2Fbookbook/lists"}