{"id":13609315,"url":"https://github.com/borkdude/quickblog","last_synced_at":"2025-04-12T23:42:07.327Z","repository":{"id":45723530,"uuid":"514228992","full_name":"borkdude/quickblog","owner":"borkdude","description":"Light-weight static blog engine for Clojure and babashka","archived":false,"fork":false,"pushed_at":"2025-03-10T10:08:03.000Z","size":148,"stargazers_count":186,"open_issues_count":7,"forks_count":34,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-12T23:42:02.392Z","etag":null,"topics":["babashka","clojure","static-site-generator"],"latest_commit_sha":null,"homepage":"https://blog.michielborkent.nl/","language":"Clojure","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/borkdude.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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},"funding":{"github":"borkdude"}},"created_at":"2022-07-15T10:31:08.000Z","updated_at":"2025-04-07T13:23:33.000Z","dependencies_parsed_at":"2023-02-18T16:46:00.705Z","dependency_job_id":"f56f72c2-0f6b-4804-aa40-a549df0736f1","html_url":"https://github.com/borkdude/quickblog","commit_stats":{"total_commits":103,"total_committers":16,"mean_commits":6.4375,"dds":"0.44660194174757284","last_synced_commit":"1b3941a3d07e1419c3584361e1cea4930a52fdf3"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fquickblog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fquickblog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fquickblog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fquickblog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/borkdude","download_url":"https://codeload.github.com/borkdude/quickblog/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647257,"owners_count":21139081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["babashka","clojure","static-site-generator"],"created_at":"2024-08-01T19:01:34.070Z","updated_at":"2025-04-12T23:42:07.305Z","avatar_url":"https://github.com/borkdude.png","language":"Clojure","funding_links":["https://github.com/sponsors/borkdude"],"categories":["Clojure"],"sub_categories":[],"readme":"# Quickblog\n\nThe blog code powering my [blog](https://blog.michielborkent.nl/).\n\nSee [API.md](API.md) on how to use this.\n\nCompatible with [babashka](#babashka) and [Clojure](#clojure).\n\nIncludes hot-reload. See it in action [here](https://twitter.com/borkdude/status/1547912740156583936).\n\n## Blogs using quickblog\n\nInstances of quickblog can be seen here:\n\n- [Michiel Borkent's blog](https://blog.michielborkent.nl)\n- [Josh Glover's blog](https://jmglov.net/blog)\n- [Jeremy Taylor's blog](https://jdt.me/strange-reflections.html)\n- [JP Monetta's blog](https://jpmonettas.github.io/my-blog/public/)\n- [Luc Engelen's blog](https://blog.cofx.nl/) - ([source](https://github.com/cofx22/blog))\n- [Rattlin.blog](https://rattlin.blog/)\n- [REP‘ti’L‘e’](https://kuna.us/)\n- [Søren Sjørup's blog](https://zoren.dk)\n- [Henry Widd's blog](https://widdindustries.com/blog)\n- [Anders means different](https://www.eknert.com/blog) - ([source](https://github.com/anderseknert/blog))\n- [Kira McLean's programming blog](https://codewithkira.com) - ([source](https://github.com/kiramclean/kiramclean.github.io))\n- [Ed Porras' blog](https://digressed.net)\n- [Saket Patel](https://blog.saketpatel.me/)\n- [Paul Butcher's blog](https://paulbutcher.com/)\n- [Alex Sheluchin's blog](https://fnguy.com/) - ([source](https://github.com/sheluchin/blog))\n\n### Articles about quickblog\n\n- [Quickblog by Anders Means Different](https://www.eknert.com/blog/quickblog)\n\nFeel free to PR yours.\n\n## Quickstart\n\n### Babashka\n\nquickblog is meant to be used as a library from your Babashka project. The\neasiest way to use it is to add a task to your project's `bb.edn`.\n\nThis example assumes a basic `bb.edn` like this:\n\n``` clojure\n{:deps {io.github.borkdude/quickblog\n        #_\"You use the newest SHA here:\"\n        {:git/sha \"3a1d6aff07f692f6e62606317f3d9e981b1df702\"}}\n :tasks\n {:requires ([quickblog.cli :as cli])\n  :init (def opts {:blog-title \"REPL adventures\"\n                   :blog-description \"A blog about blogging quickly\"})\n  quickblog {:doc \"Start blogging quickly! Run `bb quickblog help` for details.\"\n             :task (cli/dispatch opts)}}}\n```\n\nTo create a new blog post:\n\n``` clojure\n$ bb quickblog new --file \"test.md\" --title \"Test\"\n```\n\nTo start an HTTP server and re-render on changes to files:\n\n```\n$ bb quickblog watch\n```\n\n### Clojure\n\nQuickblog can be used in Clojure with the exact same API as the bb tasks.\nDefault options can be configured in `:exec-args`.\n\n``` clojure\n:quickblog\n{:deps {io.github.borkdude/quickblog\n        #_\"You use the newest SHA here:\"\n        {:git/sha \"3a1d6aff07f692f6e62606317f3d9e981b1df702\"}\n        org.babashka/cli {:mvn/version \"0.3.35\"}}\n :main-opts [\"-m\" \"babashka.cli.exec\" \"quickblog.cli\" \"run\"]\n :exec-args {:blog-title \"REPL adventures\"\n             :blog-description \"A blog about blogging quickly\"}}\n```\n\nAfter configuring this, you can call:\n\n```\n$ clj -M:quickblog new --file \"test.md\" --title \"Test\"\n```\n\nTo watch:\n\n```\n$ clj -M:quickblog watch\n```\n\netc.\n\n## Features\n\n### Markdown\n\nPosts are written in Markdown and processed by\n[markdown-clj](https://github.com/yogthos/markdown-clj), which implements the\n[MultiMarkdown](https://github.com/fletcher/MultiMarkdown/wiki/MultiMarkdown-Syntax-Guide)\nflavour of Markdown.\n\n### Metadata\n\nPost metadata is specified in the post file using [MultiMarkdown's metadata\ntags](https://github.com/fletcher/MultiMarkdown/wiki/MultiMarkdown-Syntax-Guide#metadata).\nquickblog expects three pieces of metadata in each post:\n- `Title` - the title of the post\n- `Date` - the date when the post will be published (used for sorting posts, so\n  [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetimes are recommended)\n- `Tags` - a comma-separated list of tags\n\n`quickblog new` requires the title to be specified and provides sensible\ndefaults for `Date` and `Tags`.\n\nYou can add any metadata fields to posts that you want. See the [Social\nsharing](#social-sharing) section below for some useful suggestions.\n\n**Note: metadata may not include newlines!**\n\n### favicon\n\n**NOTE:** when enabling or disabling a favicon, you must do a full re-render of\nyour site by running `bb quickblog clean` and then your `bb quickblog render`\ncommand.\n\nTo enable a [favicon](https://en.wikipedia.org/wiki/Favicon), add `:favicon\ntrue` to your quickblog opts (or use `--favicon true` on the command line).\nquickblog will render the contents of `templates/favicon.html` and insert them\nin the head of your pages.\n\nYou will also need to create the favicon assets themselves. The easiest way is\nto use a favicon generator such as\n[RealFaviconGenerator](https://realfavicongenerator.net/), which will let you\nupload an image and then gives you a ZIP file containing all of the assets,\nwhich you should unzip into your `:assets-dir` (which defaults to `assets`).\n\nYou can read an example of how to prepare a favicon here:\nhttps://jmglov.net/blog/2022-07-05-hacking-blog-favicon.html\n\nquickblog's default template expects the favicon files to be named as follows:\n- `android-chrome-192x192.png`\n- `android-chrome-512x512.png`\n- `apple-touch-icon.png`\n- `browserconfig.xml`\n- `favicon-16x16.png`\n- `favicon-32x32.png`\n- `favicon.ico`\n- `mstile-150x150.png`\n- `safari-pinned-tab.svg`\n- `site.webmanifest`\n\nIf any of these files are not present in your `:assets-dir`, a quickblog default\nwill be copied there from `resources/quickblog/assets`.\n\n### Social sharing\n\nSocial media sites such as Facebook, Twitter, LinkedIn, etc. display neat little\npreview cards when you share a link. These cards are populated from certain\n`\u003cmeta\u003e` tags (as described in \"[How to add a social media share card to any\nwebsite](https://dev.to/mishmanners/how-to-add-a-social-media-share-card-to-any-website-ha8)\",\nby Michelle Mannering) and typically contain a title, description / summary, and\npreview image.\n\nquickblog's [base\ntemplate](https://github.com/borkdude/quickblog/blob/389833f393e04d4176ef3eaa5047fa307a5ff2e8/resources/quickblog/templates/base.html)\nadds meta tags for the page title for all pages and descriptions for the\nfollowing pages:\n- Index: `{{blog-description}}`\n- Archive: Archive - `{{blog-description}}`\n- Tags: Tags - `{{blog-description}}`\n- Tag pages: Posts tagged \"`{{tag}}`\" - `{{blog-description}}`\n\nIf you specify a `:blog-image URL` option, a preview image will be added to the\nindex, archive, tags, and tag pages. The URL should point to an image; for best\nresults, the image should be 1200x630 and maximum 5MB in size. It may either be\nan absolute URL or a URL relative to `:blog-root`.\n\nFor post pages, meta tags will be populated from `Title`, `Description`,\n`Image`, `Image-Alt`, and `Twitter-Handle` metadata in the document.\n\nIf not specified, `Twitter-Handle` defaults to the `:twitter-handle` option to\nquickblog. The idea is that the `:twitter-handle` option is the Twitter handle\nof the person owning the blog, who is likely also the author of most posts on\nthe blog. If there's a guest post, however, the guest blogger can add their\nTwitter handle instead.\n\nFor example, a post could look like this:\n\n``` text\nTitle: Sharing is caring\nDate: 2022-08-16\nTags: demo\nImage: assets/2022-08-16-sharing-preview.png\nImage-Alt: A leather-bound notebook lies open on a writing desk\nTwitter-Handle: quickblog\nDescription: quickblog now creates nifty social media sharing cards / previews. Read all about how this works and how you can maximise engagement with your posts!\n\nYou may have already heard the good news: quickblog is more social than ever!\n...\n```\n\nThe value of the `Image` field is either an absolute URL or a URL relative to\n`:blog-root`. As noted above, images should be 1200x630 and maximum 5MB in size\nfor best results.\n\n`Image-Alt` provides alt text for the preview image, which is extremely\nimportant for making pages accessible to people using screen readers. I highly\nrecommend reading resources like \"[Write good Alt Text to describe\nimages](https://accessibility.huit.harvard.edu/describe-content-images)\" to\nlearn more.\n\nResources for understanding and testing social sharing:\n- [Meta Tags debugger](https://metatags.io/)\n- [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/)\n- [LinkedIn Post Inspector](https://www.linkedin.com/post-inspector/)\n- [Twitter Card Validator](https://cards-dev.twitter.com/validator)\n\n### Linking to previous and next posts\n\nIf you set the `:link-prev-next-posts` option to `true`, quickblog adds `prev`\nand `next` metadata to each post (where `prev` is the previous post and `next`\nis the next post in date order, oldest to newest). You can make use of these by\nadding something similar to this to your `post.html` template:\n\n``` html\n{% if any prev next %}\n  \u003cdiv class=\"post-prev-next\"\u003e\n{% if prev %}\n    \u003cdiv\u003e⏪ \u003ca href=\"{{prev.file|replace:.md:.html}}\"\u003e{{prev.title}}\u003c/a\u003e\u003c/div\u003e\n{% endif %}\n{% if next %}\n    \u003cdiv\u003e\u003ca href=\"{{next.file|replace:.md:.html}}\"\u003e{{next.title}}\u003c/a\u003e ⏩\u003c/div\u003e\n{% endif %}\n  \u003c/div\u003e\n{% endif %}\n```\n\n## Templates\n\nquickblog uses the following templates in site generation:\n- `base.html` - All pages. Page body is provided by the `{{body}}` variable.\n- `post.html` - Post bodies.\n- `style.css` - Styles for all pages.\n- `favicon.html` - If `:favicon true`, used to include favicon in the `\u003chead\u003e`\n  of all pages.\n- `tags.html` - Tag overview page.\n- `post-links.html` - Used to render lists of blog posts in the archive and\n  each page corresponding to a single tag.\n- `index.html` - Index page. Posts containing the marker comment\n  `\u003c!-- end-of-preview --\u003e` are included on the index page up until the first\n  occurrence of that comment.\n\nquickblog looks for these templates in your `:templates-dir`, and if it doesn't\nfind them, will copy a default template into that directory. It is recommended\nto keep `:templates-dir` under revision control so that you can modify the\ntemplates to suit your needs and preferences.\n\nThe default templates are occasionally modified to support new features. When\nthis happens, you won't be able to use the new feature without making the same\nmodifications to your local templates. The easiest way to do this is to run `bb\nquickblog refresh-templates`.\n\n### New posts\n\nIn addition to the HTML templates above, you can also use a template for\ngenerating new posts. Assuming you have a template `new-post.md` that looks like\nthis:\n\n``` markdown\nTitle: {{title}}\nDate: {{date}}\nTags: {{tags|join:\\\",\\\"}}\nImage: {% if image %}{{image}}{% else %}{{assets-dir}}/{{file|replace:.md:}}-preview.png{% endif %}\nImage-Alt: {{image-alt|default:FIXME}}\nDiscuss: {{discuss|default:FIXME}}\n{% if preview %}Preview: true\\n{% endif %}\nWrite a blog post here!\n```\n\nyou can generate a new post like this:\n\n``` text\n$ bb quickblog new --file \"test.md\" --title \"Test\" --preview --template-file new-post.md\n```\n\nAnd the resulting `posts/test.md` will look like this:\n\n``` markdown\nTitle: Test\nDate: 2024-01-19\nTags: clojure\nImage: assets/test-preview.png\nImage-Alt: FIXME\nDiscuss: FIXME\nPreview: true\n\nWrite a blog post here!\n```\n\n**It is not recommended to keep your new post template in your templates-dir, as\nany changes to the new post template will cause all of your existing posts to be\nre-rendered, which is probably not what you want!**\n\n## Breaking changes\n\n### posts.edn removed\n\nquickblog now keeps metadata for each blog post in the post file itself. It used\nto use a `posts.edn` file for this purpose. If you are upgrading from a version\nthat used `posts.edn`, you should run `bb quickblog migrate` and then remove the\n`posts.edn` file.\n\n## Improvements\n\nFeel free to send PRs for improvements.\n\nMy wishlist:\n\n- There might be a few things hardcoded that still need to be made configurable.\n- Upstream improvements to [markdown-clj](https://github.com/yogthos/markdown-clj)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fborkdude%2Fquickblog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fborkdude%2Fquickblog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fborkdude%2Fquickblog/lists"}