{"id":13510198,"url":"https://github.com/chr15m/motionless","last_synced_at":"2025-08-08T01:32:43.375Z","repository":{"id":57303098,"uuid":"320711400","full_name":"chr15m/motionless","owner":"chr15m","description":"Generate static sites with code.","archived":false,"fork":false,"pushed_at":"2021-01-24T08:41:59.000Z","size":21,"stargazers_count":77,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-22T15:43:36.854Z","etag":null,"topics":["dom-api","dom-manipulation","html","hyperscript","javascript","jsdom","node","node-js","node-module","nodejs","ssg","static-site-generator"],"latest_commit_sha":null,"homepage":"","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/chr15m.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":"2020-12-12T00:02:13.000Z","updated_at":"2025-07-15T00:23:26.000Z","dependencies_parsed_at":"2022-08-29T09:52:24.029Z","dependency_job_id":null,"html_url":"https://github.com/chr15m/motionless","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chr15m/motionless","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chr15m%2Fmotionless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chr15m%2Fmotionless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chr15m%2Fmotionless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chr15m%2Fmotionless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chr15m","download_url":"https://codeload.github.com/chr15m/motionless/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chr15m%2Fmotionless/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269352444,"owners_count":24402668,"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","status":"online","status_checked_at":"2025-08-07T02:00:09.698Z","response_time":73,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["dom-api","dom-manipulation","html","hyperscript","javascript","jsdom","node","node-js","node-module","nodejs","ssg","static-site-generator"],"created_at":"2024-08-01T02:01:28.319Z","updated_at":"2025-08-08T01:32:43.338Z","avatar_url":"https://github.com/chr15m.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","static-site-generator"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.svg?sanitize=true\" alt=\"motionless logo\"\u003e\u003cbr/\u003e\n\u003c/p\u003e\n\n**Motionless** is a static site generator for making websites using plain JavaScript and HTML with Node. There's no special templating or config language to learn. Instead you load plain HTML files and then use querySelectors and the DOM API to change pages using JavaScript, just like you would in the browser. Motionless has batteries included - it bundles dependencies for doing static site stuff like **markdown rendering**, **parsing HTML**, **reading and writing files**, **querySelectors**, etc. The result is a concise JavaScript API to specify exactly how to turn your HTML file into multiple web pages with different content. Motionless has a bias towards shipping sites fast and tries to save on typing.\n\n# Install\n\n`npm i motionless`\n\n# Use\n\nThe fastest way to get started is with the [motionless-site node template](https://github.com/chr15m/create-motionless-site):\n\n```\nnpm i motionless-site YOUR-SITE-NAME\ncd YOUR-SITE-NAME\n```\n\nThis will create a new project in `YOUR-SITE-NAME` that use motionless to build pages.\n\nTo start from scratch instead, there is a minimal working example in the [example](./example) folder. You can use the `live-reload` package (`npm i live-reload`) to serve your build directory while you build your static site.\n\n## Static site recipes\n\nThe following are some recipes for doing common static site tasks.\n\nIn this first example we load an HTML file and a markdown file and replace the `\u003cmain\u003e` element with a rendered version of the markdown.\n\n```javascript\nconst m = require('motionless');\n\n// load and parse index.html to use as a template\nconst template = m.dom(m.load(\"index.html\"));\n// load README.md and render it to an HTML string\nconst content = m.md(m.load(\"README.md\"));\n// set the \u003cmain\u003e tag contents to the rendered markdown\ntemplate.$(\"main\").innerHTML = content;\n// save the whole thing into the file readme.html\nm.save(\"readme.html\", template.render());\n```\n\nHere's another example building a table of contents from all of the `\u003ch2\u003e` tags on the page. We use the [hyperscript](https://github.com/hyperhype/hyperscript) helper to generate HTML using JavaScript code.\n\n```javascript\n// get a list of the h2 headers in the page\nconst headers = template.$$(\"h2\");\nif (headers.length) {\n  // build a list of \u003cli\u003e tags with an \u003ca\u003e link for every header\n  const items = headers.map((h2)=\u003e{\n    // add a named href the TOC can link to\n    h2.appendChild(t.h(\"a\", {\"className\": \"pilcrow\", \"name\": m.slug(h2.textContent)}, \" \"))\n    // create the TOC \u003cli\u003e link tag\n    return t.h(\"li\", {}, t.h(\"a\", {\"href\": \"#\" + m.slug(h2.textContent)}, h2.textContent));\n  });\n  // create the top level \u003cul\u003e tag containing the items\n  const toc = template.h(\"ul\", {\"className\": \"toc\"}, items);\n  // prepend the table of contents inside the \u003cmain\u003e tag\n  template.$(\"main\").prepend(toc);\n}\n```\n\nA final example showing how to load up several markdown pages and render them using a single template.\n\n```javascript\nm = require('motionless');\n\n// load our basic page\ntemplate = m.dom(m.load(\"index.html\"));\n\n// list the content folder full of markdown files\nm.dir(\"content\").forEach(function(pagefile) {\n  const page = m.load(\"content/\" + pagefile);\n  const title = pagefile.replace(/\\-/g, \" \").replace(\".md\", \"\");\n  // set the main part of the template, the title, and the h1 tag\n  template.$(\"main\").innerHTML = m.md(page);\n  template.$(\"title\").textContent = title;\n  template.$(\"h1\").textContent = title;\n  // save the updated HTML file\n  m.save(pagefile.replace(\".md\", \".html\"), template.render());\n});\n```\n\n# API\n\n## `m.load(path)`\n\nReads the entire file at `path` and returns a string synchronously.\n\n## `m.save(path, contents)`\n\nA simple alias for `writeFileSync` to write the string `contents` into the file at `path`.\n\n## `m.dir(path)`\n\nA simple alias for `readdirSync` to get a listing of files in the directory at `path`.\n\n## `m.glob(globstring)`\n\nAn instance of [node-glob](https://github.com/isaacs/node-glob) to provide a convenient way to generate file listings using wildcards etc.\n\n## `m.dom(html)`\n\nParses the `html` string using `jsdom` and returns a [jsDOM](https://www.npmjs.com/package/jsdom) object that behaves just like a [browser HTML `Node`](https://developer.mozilla.org/en-US/docs/Web/API/Node).\n\nFor convenience the returned jsdom object has some useful properties set:\n\n * `template.doc` - convenient access to the `document` object.\n * `template.$` - a shortcut for `template.doc.querySelector` returning a single HTML `Node`.\n * `template.$$` - a shortcut for `querySelectorAll` returning a JavaScript array of HTML `Node`s.\n * `template.h` - a [hyperscript](https://github.com/hyperhype/hyperscript) instance you can use to build new DOM elements.\n * `template.render()` - convenience function for returning the HTML string of the rendered document.\n\n## `m.remove(element)`\n\nConvenience function to remove HTML `element` from it's parent node.\n\n## `m.md(markdown)`\n\nRender the `markdown` string to an HTML string. If you want to modify the rendered markdown using the DOM API just wrap it with `m.dom()` like this:\n\n```javascript\nconst content = m.dom(m.md(m.load(\"README.md\")));\n```\n\n## `m.minify(various)`\n\nAn instance of [minify](https://coderaiser.github.io/minify/) that you can use to crunch various types of files. For example CSS and JavaScript:\n\n```javascript\nconst styles = await minify('style.css');\nconst js = await minify('main.js').catch(console.error);\n```\n\n## `m.slug(text)`\n\nConverts `text` into a URL-friendly path, for example: `My Wonderful Webpage` becomes `my-wonderful-webpage`.\n\n# Who\n\nHi, 👋 I'm Chris and I made this. You can find me online at [mccormick.cx](https://mccormick.cx/) and [@mccrmx](https://twitter.com/mccrmx). I made this so I can ship static websites fast using Node.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchr15m%2Fmotionless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchr15m%2Fmotionless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchr15m%2Fmotionless/lists"}