{"id":20507223,"url":"https://github.com/nombrekeff/hobo-js","last_synced_at":"2025-04-13T21:32:41.119Z","repository":{"id":197308369,"uuid":"696445899","full_name":"nombrekeff/hobo-js","owner":"nombrekeff","description":"Little library to generate any HTML with JavaScript/TypeScript.","archived":false,"fork":false,"pushed_at":"2025-01-17T10:43:02.000Z","size":944,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T11:50:29.551Z","etag":null,"topics":["css","from-script","generate","html","markup","render","ssr","typescript"],"latest_commit_sha":null,"homepage":"https://nombrekeff.github.io/hobo-js/","language":"TypeScript","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/nombrekeff.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}},"created_at":"2023-09-25T18:58:39.000Z","updated_at":"2025-01-17T10:43:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"9c6f153a-2734-4a0d-904b-fb8db361abb8","html_url":"https://github.com/nombrekeff/hobo-js","commit_stats":null,"previous_names":["nombrekeff/hobo-js"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nombrekeff%2Fhobo-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nombrekeff%2Fhobo-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nombrekeff%2Fhobo-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nombrekeff%2Fhobo-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nombrekeff","download_url":"https://codeload.github.com/nombrekeff/hobo-js/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248786222,"owners_count":21161418,"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":["css","from-script","generate","html","markup","render","ssr","typescript"],"created_at":"2024-11-15T20:03:52.989Z","updated_at":"2025-04-13T21:32:41.112Z","avatar_url":"https://github.com/nombrekeff.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Hobo.js\n![](./hobo-header.png)\n\n\nWelcome to Hobo. A little utility to generate html inside your js/ts code. Meant as a side-project, but after writing it I thought it might be useful to some people in some scenarios.\n\n\n### Who's this for?\n\nI have no idea! I might use it some time. But if you use it, and feel like letting me know, you can either leave a star, contribute or reach out! I would be interested in knowing how it's being used, if at all!\n\n### What does it do?\n\nWell, in essence it allows us to create html documents inside js or ts with ease. If I've not missed anything obvious, I think it's possible to generate any kind of html document. Or maybe other stuff like XML too 🤷🏻‍♂️\n\nYou can generate any tag you want. Add **classes**, **ids**, **styles**, and **attributes**. Create **css** and add **scripts**. All of this, with a quite simple api. I know it might look weird at first, but it's quite intuitive when you gget the hang (_it's quite fast, api is quite small_). \n\nIt even handles self closing tags (void elements) for you. So when creating a **img** tag, it will know it's self closing.\n\nAdditionally it's almost fully typed, not only the surface, but into most css property values! IDEs will help autocomplete a lot of stuff! And is extensively tested.\n\n\u003e NOTE: If you want a similar API but for creating apps at Runtime, take a look at my other project [Cardboard](https://github.com/nombrekeff/cardboard-js).\n\n### Getting Started\nInstall package: \n\n```\nnpm install https://github.com/nombrekeff/hobo-js\nnpm install hobo-js\n```\n\nThen you  can import the package. \n\n```ts\nimport { builders, generate } from 'hobo-js';\n// Or\nconst { builders, generate } = require('hobo-js')\n```\n\nI recommend destructuring builders, for cleaner code:\n\n```ts\nconst { div, p, span, b, script, button, style, a, hr } = builders;\n```\n\n### Examples\n\nCheck out the [`examples`](/examples) folder for a variety of examples on how to use hobo.\n\nYou can check the interactive demo [here](https://nombrekeff.github.io/hobo-interactive-demo/)\n\n### Docs\nYou can find docs [here](https://nombrekeff.github.io/hobo-js/)\n\n### Demo\n\nLet me show you a little sample (_I explain everything in detail below_)\n\n```ts\nconst myPage = doc('My Page Title');\n\nmyPage.head.append(\n  style({\n    '.wrapper': {\n      background: 'black',\n      display: 'flex',\n      alignItems: 'center',\n      justifyContent: 'center',\n      ':hover': {\n        background: 'red'\n      }\n    },\n  }, {/* more style objects */}),\n);\n\ndiv.attach.ac('wrapper').build(\n  p(\"I'm a child of div.wrapper\"),\n  b.addStyle('color', 'aliceblue')('And so am I'),\n  hr,\n  a.addAttr('href', 'http://example.com').b('Click me'),\n  button.id('button-id').b(\"I'm also a child\"),\n);\n\nscript.a(() =\u003e {\n  const btn = document.querySelector('#button-id');\n}, () =\u003e {/* more js */});\n\nconsole.log(generate(myPage.doc));\n```\n\nThe above snippet would output the following html:\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eMy Page Title\u003c/title\u003e\n    \u003cstyle\u003e\n      .wrapper {\n        background: black;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n      }\n      .wrapper:hover {\n        background: red;\n      }\n    \u003c/style\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv class=\"wrapper\"\u003e\n      \u003cp\u003eI'm a child of div.wrapper\u003c/p\u003e\n      \u003cb style=\"color: aliceblue\"\u003eAnd so am I\u003c/b\u003e\n      \u003chr /\u003e\n      \u003ca href=\"http://example.com\"\u003eClick me\u003c/a\u003e\u003cbutton id=\"button-id\"\u003eI'm also a child\u003c/button\u003e\n    \u003c/div\u003e\n    \u003cscript\u003e\n      const btn = document.querySelector('#button-id');\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n#### Demo explanation\n\n```ts\nconst myPage = doc('My Page Title');\n```\n\nFirst of all we create an HTML Page, by calling `doc()`. This will create an HTML, head and body tags. And returns 3 tags, `doc`, `head` and `body`. You can pass in an optional `title`, and `attachMode` arguments (_look at the [Attaching](#attaching) section for more info_).\n\n\u003e It's not required to create a doc, you can start the document with any tag you want.\n\u003e It also \"attaches\" the `body` tag to the hobo context. This means that you can then automatically add tags to the attached tag without having to use `.append`. I will explain further down.\n\u003e * `doc(mode)`, the doc can receive an argument to change the Attach behaviour. You can specify if you want to attach to the body, head or HTML tags. It will attach to the body by default\n\n----\n\n```ts\nmyPage.head.append(\n  style({\n    '.wrapper': {\n      background: 'black',\n      display: 'flex',\n      alignItems: 'center',\n      justifyContent: 'center',\n      ':hover': {\n        background: 'red'\n      }\n    },\n  }, {\n    'div': {...}\n  }),\n);\n```\n\nThe above code adds a style tag to the `head` tag. The style tag accepts objects with css definitions. It's typed, so intelisense will work.\nYou can pass any number of style objects. And you can also nest like in SCSS.\n\nFor example, this:\n```ts\n'.wrapper': {\n  background: 'blue'\n  ':hover': {\n    background: 'red'\n  }\n}\n```\n\nWill generate: \n```css\n.wrapper { background: 'blue' }\n.wrapper:hover { background: 'red' }\n```\n\n\u003e Note that in this case we use `.append` as the head is not attached.\n\n----\n\n```ts\ndiv.a.ac('wrapper').build(\n  p(\"I'm a child of div.wrapper\"),\n  b.addStyle('color', 'aliceblue')('And so am I'),\n  hr,\n  a.addAttr('href', 'http://example.com').b('Click me'),\n  button.id('button-id').b(\"I'm also a child\"),\n);\n```\n\nIn the step above step, we create the HTML that will be inside the `body`. As you can see instead of calling `.append` in the body tag like with the style.\n`.a` and `.attach` are used to [attach](#attaching) to the current hobo context's attached tag (_which will be the body tag in the example_)\n\nThen we set the tag's class name by calling `.ac` (you can also use `.addClass()`), this will set the class wrapper to the div (`\u003cdiv class=\"wrapper\"\u003e\u003c/div\u003e`)\n\nAfter that, we build the tag by calling `.b` (or `.build()`, or you can also call the builder `div()`), and pass in a list of children.\n\nHobo uses the builder pattern to ease the creation of tags. A tag can be built by, either calling the builder directly:\n```ts\np(\"I'm a child of div.wrapper\");\n```\nor by calling the `.b` method:\n```ts\nb.build('And so am I'),\n```\n\n\u003e NOTE that it's not required to build the tag if you don't need to pass children to it:\n\u003e ```ts \n\u003e div(\n\u003e   hr,\n\u003e   div.addClass('white-box'), \n\u003e )\n\u003e ```\n\n\u003e **NOTE** Each time you modify the builder it returns a new TagBuilder instance. \n\u003e So you can't assign it and then modify it, you need to chain. \n\u003e This is done so Hobo can generate new tag builders for each tag without needing to be a function.\n\u003e \n\u003e This does not work:\n\u003e ```ts \n\u003e const tag = div.ac('cl'); // .ac is shorthand for addClass\n\u003e tag.append(p()); // Will not affect `tag`\n\u003e ```\n\n\u003e Instead use chaining:\n\u003e ```ts \n\u003e const tag = div.ac('cl').append(p());\n\u003e ```\n\nYou can set any attribute by using `.aa`, or `.am`:\n```ts\na.addAttr('href', 'http://example.com');\na.aa('href', 'http://example.com');\na.setAttr({ 'href': 'http://example.com' });\na.sa({ 'href': 'http://example.com' });\n```\n\n----\n\n```ts\np.addStyle('color', 'aliceblue');\np.as('color', 'aliceblue');\np.setStyle({ 'color': 'aliceblue' });\np.ss({ 'color': 'aliceblue' });\n```\nTags can also have inline styles. You can add a single style using the add style (`.as`) method, or add multiple at once using the set styles (`.ss`) method.\n\n----\n\n```ts\nscript.a(() =\u003e {\n  const btn = document.querySelector('#button-id');\n}, () =\u003e {\n  // More JS\n});\n```\n\nCreates a `script` tag. Anything inside the function will be inserted into the generated script.  \nThe script accepts a list of functions. You will also have complete typing for dom.\n\n----\n\n```ts\nconsole.log(generate(myPage.doc));\n```\n\nFinally, generate the html. `generate` returns a string. It's up to you to handle it from here. \n\n\u003e NOTE that generate must receive the root tag you want to generate. \n\u003e In this example we pass in `myPage.doc` to generate the whole page.\n\u003e\n\u003e But you can generate any tag you want:\n\u003e ```ts\n\u003e generate(div(p('Hello'), p('world')));\n\u003e ```\n\n\n### Attaching\n\nby calling `attach` on a tag, it makes it so that you don't need to manually add children to a tag:\n\n```ts\nconst root = div.build(\"I'm the root tag\");\n\nattach(root);\ndiv.a;\ndiv.attach;\np.attach;\n```\n\n\u003e Any tag that calls `.a` or `.attach` will be added as children of the root div.\n\nYou can also attach multiple times:\n```ts\nconst root = div.build(\"I'm the root tag\");\n\nattach(root);\ndiv.a;\nconst child = div.attach.addClass('child-wrapper');\n\nattach(child);\np.a(\"I'm a child of child-wrapper\");\np.a(\"And so am I\");\ndetach();\n\np.a(\"I'm now a child of root div again\");\ndetach();\n\np.a(\"I'm now not attached as there are no attached tags\");\n```\n\nBy calling attach the last tag is attached to, then by calling detach, the previously attached tag is now attached. Note that if you call attach and there's only 1 tag attached, it will detach that also. So consequent tags that try to attach will not attach as there's no tag attached.\n\nWhen calling `doc()`, it will automatically attach to the body. Although you can specify the tag you want to attach to by passing the attachMode argument.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnombrekeff%2Fhobo-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnombrekeff%2Fhobo-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnombrekeff%2Fhobo-js/lists"}