{"id":13509146,"url":"https://github.com/ijcd/taggart","last_synced_at":"2026-02-21T12:01:28.634Z","repository":{"id":62430347,"uuid":"103509088","full_name":"ijcd/taggart","owner":"ijcd","description":"HTML as code in Elixir","archived":false,"fork":false,"pushed_at":"2018-04-15T22:53:15.000Z","size":61,"stargazers_count":35,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-11-23T03:24:32.919Z","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":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ijcd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-14T08:46:59.000Z","updated_at":"2025-07-10T00:50:26.000Z","dependencies_parsed_at":"2022-11-01T20:19:56.401Z","dependency_job_id":null,"html_url":"https://github.com/ijcd/taggart","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/ijcd/taggart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijcd%2Ftaggart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijcd%2Ftaggart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijcd%2Ftaggart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijcd%2Ftaggart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ijcd","download_url":"https://codeload.github.com/ijcd/taggart/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ijcd%2Ftaggart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29680147,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T11:29:27.227Z","status":"ssl_error","status_checked_at":"2026-02-21T11:29:20.292Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":"2024-08-01T02:01:03.569Z","updated_at":"2026-02-21T12:01:28.604Z","avatar_url":"https://github.com/ijcd.png","language":"Elixir","funding_links":[],"categories":["Templating"],"sub_categories":[],"readme":"# Taggart\n\n[![Hex.pm](https://img.shields.io/hexpm/v/taggart.svg)](https://hex.pm/packages/taggart)\n[![Build Docs](https://img.shields.io/badge/hexdocs-release-blue.svg)](https://hexdocs.pm/taggart/index.html)\n[![Build Status](https://travis-ci.org/ijcd/taggart.svg?branch=master)](https://travis-ci.org/ijcd/taggart)\n\nTaggart is a generation library for tag-based markup (HTML, XML, SGML,\netc.). It is useful for times when you just want code and functions, not\ntemplates. We already have great composition and abstraction tools in\nElixir. Why not use them? With this approach, template composition through\nsmaller component functions should be easy.\n\n[Documentation](http://hexdocs.pm/taggart/)\n\nThere is a [blog post](https://medium.com/@ijcd/announcing-taggart-4e62b485e882)\nwith an introduction and more documentation.\n\n## Installation\n\nThe package can be installed by adding `taggart` to your list of\ndependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:taggart, \"~\u003e 0.1.5\"}\n  ]\nend\n```\n\n## Usage\n\nTaggart produce Phoenix-compatible \"safe\" html through underlying usage of the\n[`Phoenix.HTML.content_tag/2`](https://hexdocs.pm/phoenix_html/Phoenix.HTML.Tag.html#content_tag/2).\nSince it just produces IO Lists, it should remain compatible with any\nother library that uses the same format.\n\n### Syntaxes\n\nTaggart supports a number of different syntaxes:\n\n```\nuse Taggart.HTML\n\ndiv(\"Name\")\n\ndiv(\"Name\", class: \"bold\")\n\ndiv(class: \"bold\", do: \"Name\")\n\ndiv do\nend\n\ndiv(class: \"bold\", do: \"Name\")\n\ndiv(class: \"bold\") do\n  \"Name\"\nend\n\na(href: \"#bottom\", class: \"uk-button uk-button-default\", \"i-am-a-boolean\": true), do: \"Click me!\"\n```\n\n### Nesting\n\nYou can nest and combine in expected ways:\n\n```\nuse Taggart.HTML\n\nname = \"Susan\"\nage = 27\n\nhtml do\n  body do\n    div do\n      h2 \"Buyer\"\n      p name, class: \"name\"\n      p age, class: \"age\"\n    end\n    div do\n      \"Welcome\"\n    end\n  end\nend\n```\n\n### Embedding in Phoenix Forms\n\nYou can embed Taggart inside Phoenix helpers using `Taggart.taggart/1`\nto create IO List without creating a top-level wrapping tag.\n\n```\nuse Taggart.HTML\n\nform = form_for(conn, \"/users\", [as: :user], fn f -\u003e\n  taggart do\n    label do\n      \"Name:\"\n    end\n    label do\n      \"Age:\"\n    end\n    submit(\"Submit\")\n  end\nend)\n```\n\n### Using Phoenix Helpers\n\n```\nuse Taggart.HTML\n\nhtml do\n  body do\n    div do\n      h3 \"Person\"\n      p name, class: \"name\"\n      p 2 * 19, class: \"age\"\n      form_for(build_conn(), \"/users\", [as: :user], fn f -\u003e\n        taggart do\n          label do\n            \"Name:\"\n            text_input(f, :name)\n          end\n          label do\n            \"Age:\"\n            select(f, :age, 18..100)\n          end\n          submit(\"Submit\")\n        end\n      end)\n    end\n  end\nend\n```\n\n### Using from Phoenix Views\n\nPhoenix views are just functions, so it’s possible to use pattern\nmatching directly in a view to render your pages.\n\n```\ndefmodule TaggartDemo.PageView do\n  use TaggartDemoWeb, :view\n  use Taggart.HTML\n\n  def render(\"index.html\", assigns) do\n    taggart do\n      render_header(\"My Fancy Title\")\n      render_body\n      render_footer\n    end\n  end\n\n  def render_header(title) do\n    header do\n      h1 title\n    end\n  end\n\n  def render_body do\n    main do\n      ul do\n        for i \u003c- 1..3, do: list_item(x)\n      end\n    end\n  end\n\n  def render_footer do\n    footer do\n      \"So Long Folks!!!\"\n    end\n  end\n\n  def list_item(x) do\n    \"Name: \"\n    li(x)\n  end\nend\n```\n\n## A Note On Macro Expansion\n\nThe current design allows for a very flexible call structure. However, do\nnot be tempted to think of these as normal functions. They are currently\nimplemented as macros. This allows the `do end` blocks to processed as\nif they were a list:\n\n```\ndiv do\n  \"item 1\"\n  \"item 2\"\nend\n```\n\nThe alternative would be forcing the use of actual lists, which is necessairly noisier.\n\n```\n# Not valid, do not try:\ndiv [\n  \"Item 1\",\n  \"Item 2\"\n]\n```\n\nThe trade-off, however, is that because the macros inspect the arguements\nto determine `attr/content` placement, they do not play well with all kinds\nof ASTs.\n\nThis will work:\n\n```\n# works\na = \"foo\"\ndiv(a)\n```\n\nThis will not:\n```\n# do not try this at home\na = [id: \"foo\", class: \"bar\"]\ndiv(a)\n```\n\nIf you try this, you will get an error along the lines of\n`lists in Phoenix.HTML and templates may only contain integers \nrepresenting bytes, binaries or other list`. This is because we\nmake the choice of assuming that a single, non-list argument\n(of which AST is) is content and not attrs.\n\nAs a workaround, you can either use `Phoenix.HTML.content_tag`\ndirectly, or use the special three-argument version which\nignores the first argument:\n\n```\n# try this\na = [id: \"foo\", class: \"bar\"]\ndiv(nil, a) do \"content\" end\n```\n\n## Converting from HTML\n\n### PrestoChange.io\n\nYou can use the online tool at [prestochange.io](http://www.prestochange.io).\n\n### Install taggart escript using homebrew\n\n```\nbrew install ijcd/tap/taggart\n```\n\n```\nReads HTML from stdin and writes Taggart to stdout.\n\nUsage:\n  taggart --indent \u003cn|tabs\u003e\n  taggart --help\n\nOptions:\n  -h --help  Show this message.\n  --indent   Either n (number of spaces to indent) or \"tabs\"\n```\n\n### Build taggart escript from source\n\n```\nmix escript.build\n./taggart\n```\n\n## Design\n\nThe design had two basic requirements:\n\n1. Simple Elixir-based generation of tag-based markup.\n2. Interoperate properly with Phoenix helpers.\n\nI looked at and tried a few similar libraries (Eml, Marker), but\neither wasn't able to get them to work with Phoenix helpers or had\nproblems with their approach (usage of @tag syntax in templates where\nit didn't refer to a module attribute). My goal was to keep things\nsimple.\n\n## License\n\nTaggart is released under the Apache License, Version 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fijcd%2Ftaggart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fijcd%2Ftaggart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fijcd%2Ftaggart/lists"}