{"id":21000209,"url":"https://github.com/voidlabs/htmml","last_synced_at":"2025-07-02T15:34:11.469Z","repository":{"id":57268081,"uuid":"123280124","full_name":"voidlabs/htmml","owner":"voidlabs","description":"A markup language extending HTML to enable templating specifically designed for email (supports conditional comments)","archived":false,"fork":false,"pushed_at":"2022-05-18T14:40:01.000Z","size":20,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-25T01:16:28.915Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/voidlabs.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":"2018-02-28T12:10:57.000Z","updated_at":"2023-10-27T11:24:20.000Z","dependencies_parsed_at":"2022-09-02T05:41:07.028Z","dependency_job_id":null,"html_url":"https://github.com/voidlabs/htmml","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voidlabs%2Fhtmml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voidlabs%2Fhtmml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voidlabs%2Fhtmml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voidlabs%2Fhtmml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/voidlabs","download_url":"https://codeload.github.com/voidlabs/htmml/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254248242,"owners_count":22038996,"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":[],"created_at":"2024-11-19T08:09:47.748Z","updated_at":"2025-05-14T23:31:25.355Z","avatar_url":"https://github.com/voidlabs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HTMML template language for HTML email\n\nHTMML is a very simple template language we use to make it easier to write mosaico master templates. Given the master template language uses a lot of inline styles, you end up writing a lot of repeating HTML code.\n\nHTMML processing does this:\n\n- Looks for ```\u003cdefault selector=\"\" attr1=\"\" attr2=\"\"\u003e``` tags, run the selector as a CSS/jQuery selector to find matching tags in the HTML and add the other attributes to those tags.\n\n```\n\u003ctemplate-def\u003e\n\u003cdefault selector=\".right\" align=\"right\"\u003e\n\u003c/template-def\u003e\n\u003cp class=\"right\"\u003ehello\u003c/p\u003e\n```\nwill be translated to\n```\n\u003cp class=\"right\" align=\"right\"\u003ehello\u003c/p\u003e\n```\nIt's like CSS inlining, but applied to HTML elements and HTML attributes!\n\n- ```\u003ctemplate-def\u003e\u003ctemplate\u003e``` the template-def tag contains template definitions and will be removed once template have been processed.\n\n- ```\u003ctemplate tag=\"tagname\"\u003e``` the template tag defines a new template that will be applied whenever the given tag is found in the html. Each ```\u003ctemplate\u003e``` tag contains zero, one or more ```\u003cdef\u003e``` tags and one ```\u003ctmpl\u003e``` tag. The ```\u003cdef attr=\"attrname\"\u003e``` tags define attributes supported by the template. Their ```selector=\"\"``` attributes define which tags inside the ```\u003ctmpl\u003e``` section will be altered when this attribute is declared for this template. The rest of the ```\u003cdef\u003e``` attributes will be added to the matched tags. A special ```{{attributename}}``` is available in the attributes defined to use the value of the attribute from the template caller.\n\nAn example, when this template is defined\n```\n\u003ctemplate-def\u003e\n\u003ctemplate tag=\"mytemplate\"\u003e\n  \u003cdef attr=\"myattr\" selector=\"table, .myclass\" mynewattribute=\"{{myattr}}\" /\u003e\n  \u003ctmpl\u003e\n  \t\u003ctable\u003e\u003ctr\u003e\u003ctd class=\"myclass\"\u003e{{replacedcontent}}\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n  \u003c/tmpl\u003e\n\u003c/template\u003e\n\u003c/template-def\u003e\n```\nThe following code\n```\n\u003cmytemplate myattr=\"somevalue\"\u003esome content\u003c/mytemplate\u003e\n```\nwill be translated to\n```\n\u003ctable mynewattribute=\"somevalue\"\u003e\u003ctr\u003e\u003ctd class=\"myclass\" mynewattribute=\"somevalue\"\u003esome content\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n```\n\n- Custom tags (and their templates) can be nested as long as ```{{replacedcontent}}``` is used in their ```\u003ctemplate\u003e\u003ctmpl\u003e``` definition.\n\n- Not every ```\u003cdef\u003e``` attribute is handled in the same way\n\t- ```style``` or ```addStyle``` attributes will ADD the defined style to the style declaration of the matched tag, instead of replacing it.\n\t- ```class``` attribute will ADD new classes to the classes defined in the matched tag, instead of replacing it.\n\t- ```addAttributeName``` and ```addAttributeValue``` are special attributes that can be used to add dynamically defined attributes to the matched tags.\n\n- A ```\u003crepeater\u003e``` tag is available and will simply repeat the content according to the arguments. Any html attribute found in the repeater will be read, splitted by comma (```,```) and will be available as a ```{{variable}}``` in the content of the repeater, so:\n\n```\n\u003crepeater var1=\"yellow,red,green\" var2=\"10,20,30\"\u003e\n  \u003cp style=\"color: {{var1}}\"\u003e{{var2}}\u003c/p\u003e\n\u003c/repeater\u003e\n```\nwill be translated to\n```\n  \u003cp style=\"color: yellow\"\u003e10\u003c/p\u003e\n  \u003cp style=\"color: red\"\u003e20\u003c/p\u003e\n  \u003cp style=\"color: green\"\u003e30\u003c/p\u003e\n```\n\n- Last thing: HTMML does someting tricky in order to deal with your conditional comments contents. Outlook conditional comments are often needed in email HTML code and this makes you write HTML code in special HTML comments. Being comments it is not really considered HTML by anyone but Outlook. But we want to do our HTMML stuff also on conditional comments contents, so HTMML will \"unescape\" them to bring them to HTML, then apply its own template rules/logic, and then bring back them to comments. The tags found inside comments will be prefixed by \"CC-\" before being extracted and they never create nesting, so:\n\n```\n\u003ctemplate tag=\"cctest\"\u003e\n\u003cdef attr=\"myattr\" style=\"color: {{myattr}}\" /\u003e\n\u003ctmpl\u003e\n\u003c!--[if (ie)]\u003e\u003ctable\u003e\u003ctr\u003e\u003ctd\u003e\u003c!--\u003e\n\u003cp\u003emy content\u003c/p\u003e\n\u003c!--[if (ie)]\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\u003c!--\u003e\n\u003c/tmpl\u003e\n\u003c/template\u003e\n```\nWill be temporarily translated to \n```\n\u003ctemplate tag=\"cctest\"\u003e\n\u003cdef attr=\"myattr\" selector=\"cc-table\" style=\"color: {{myattr}}\" /\u003e\n\u003cdef attr=\"wrongattr\" selector=\"cc-table p\" align=\"right\" /\u003e\n\u003ctmpl\u003e\n\u003creplacedcc condition=\"(ie)\"\u003e\u003c!-- cc:start --\u003e\u003ccc-table\u003e\u003ccc-tr\u003e\u003ccc-td\u003e\u003c!-- cc:end --\u003e\u003c/cc-td\u003e\u003c/cc-tr\u003e\u003c/cc-table\u003e\u003c/replacedcc\u003e\n\u003cp\u003emy content\u003c/p\u003e\n\u003creplacedcc condition=\"(ie)\"\u003e\u003ccc-table\u003e\u003ccc-tr\u003e\u003ccc-td\u003e\u003c!-- cc:start --\u003e\u003c/cc-td\u003e\u003c/cc-tr\u003e\u003c/cc-table\u003e\u003c!-- cc:end --\u003e\u003c/replacedcc\u003e\n\u003c/tmpl\u003e\n\u003c/template\u003e\n```\nThe def rules will be applied on this new \"template\" and then a reverse process is applied to bring back them to their original \"conditional comment\" form.\nSo the ```\u003cdef attr=\"wrongattr\"\u003e``` won't be applied, because in the \"replaced\" form the p is not INSIDE the cc-table because conditional comments do not create nesting levels.\nSo, given that template and this tag:\n```\n\u003ccctest myattr=\"red\" wrongattr=\"somethingelse\" /\u003e\n```\nthe result of HTMML translation will be\n```\n\u003c!--[if (ie)]\u003e\u003ctable style=\"color: red\"\u003e\u003ctr\u003e\u003ctd\u003e\u003c!--\u003e\n\u003cp\u003emy content\u003c/p\u003e\n\u003c!--[if (ie)]\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\u003c!--\u003e\n```\nYou see the style have been applied to the table, even if it was in a comment.\n\n### Done\n\nThe \"HTMML\" specification is complete. They are 200 lines of code and this matched my templating needs better than existing HTML template language.\n\n### HTMML users\n\nWe developed HTMML in order to improve maintenance of our [Mosaico Versafix-1 master template](https://github.com/voidlabs/versafix-template)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoidlabs%2Fhtmml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvoidlabs%2Fhtmml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoidlabs%2Fhtmml/lists"}