{"id":21029430,"url":"https://github.com/sirtimbly/frets","last_synced_at":"2025-05-15T11:31:58.437Z","repository":{"id":32065939,"uuid":"129475526","full_name":"sirtimbly/frets","owner":"sirtimbly","description":"UI Rendering Library following the SAM pattern - Functional Reactive TypeScript","archived":false,"fork":false,"pushed_at":"2023-04-24T22:28:39.000Z","size":11361,"stargazers_count":4,"open_issues_count":14,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-29T10:47:55.294Z","etag":null,"topics":["maquette","sam","sam-pattern","typescript"],"latest_commit_sha":null,"homepage":"","language":"HTML","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/sirtimbly.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","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}},"created_at":"2018-04-14T02:56:03.000Z","updated_at":"2023-01-20T13:50:14.000Z","dependencies_parsed_at":"2024-10-21T19:51:32.240Z","dependency_job_id":null,"html_url":"https://github.com/sirtimbly/frets","commit_stats":{"total_commits":130,"total_committers":2,"mean_commits":65.0,"dds":"0.15384615384615385","last_synced_commit":"58fe54a2979730b14a58f313023551a3de444c78"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirtimbly%2Ffrets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirtimbly%2Ffrets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirtimbly%2Ffrets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirtimbly%2Ffrets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sirtimbly","download_url":"https://codeload.github.com/sirtimbly/frets/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253391177,"owners_count":21900943,"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":["maquette","sam","sam-pattern","typescript"],"created_at":"2024-11-19T12:12:23.155Z","updated_at":"2025-05-15T11:31:54.524Z","avatar_url":"https://github.com/sirtimbly.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FRETS (Functional, Reactive, entirely TypeScript)\n## An Ultralight Composable Frontend TypeScript Web Framework\n\u003cp style=\"text-align: center\"\u003e\n      \n  \u003cimg alt=\"GitHub Workflow Status (master)\" src=\"https://img.shields.io/github/actions/workflow/status/sirtimbly/frets/workflows/integrate.yml?branch=master\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/frets\"\u003e\u003cimg src=\"https://badge.fury.io/js/frets.svg\" alt=\"npm version\" height=\"20\"\u003e\u003c/a\u003e\n  \u003cimg alt=\"Libraries.io dependency status for latest release\" src=\"https://img.shields.io/librariesio/release/npm/frets\"\u003e\n  \u003ca href=\"https://codecov.io/gh/sirtimbly/frets\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/sirtimbly/frets/branch/master/graph/badge.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/result?p=frets\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/minzip/frets\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://packagephobia.now.sh/result?p=frets\"\u003e\n    \u003cimg id=\"\" src=\"https://packagephobia.now.sh/badge?p=frets\" alt=\"install size\" class=\"\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://app.fossa.io/projects/git%2Bgithub.com%2Fsirtimbly%2Ffrets?ref=badge_small\" alt=\"FOSSA Status\"\u003e\u003cimg src=\"https://app.fossa.io/api/projects/git%2Bgithub.com%2Fsirtimbly%2Ffrets.svg?type=small\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/semantic-release/semantic-release\"\u003e\u003cimg src=\"https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg\"\u003e\u003c/a\u003e\n\n\u003c/p\u003e\n\n![FRETS logo](http://uploads.timbendt.com.s3.amazonaws.com/dropzone/fretslogo4@1x.png)\n\nA chainable API of programmer-friendly abstractions for building User Interfaces without HTML Templates or JSX. Enjoy the safety and productivity of TypeScript in your UI code.\n\n## Getting Started\n\n`npm install --save frets`\n\nUse [the Starter Project](https://github.com/sirtimbly/frets-starter) to get going quickly.\n\nRead the [docs](https://sirtimbly.github.io/frets/)!\n\nThe basic SAM (State Action Model) app lifecycle:\nAction (event) -\u003e Model (update) -\u003e State (calculate) -\u003e View (render) -\u003e [wait for client events that call an Action()]\n\nNote:\n\u003e Loosely based on [sam.js.org](https://sam.js.org)\n\n\n## Philosophical Rules\n\n1. Optimize for Developer Ergonomics (chainable APIs)\n2. No Magic Strings\n3. No Configuration Objects\n4. Encourage Some Functional Programming\n\n## Contact Me\n\nLeave an issue here with any questions or suggestions, or reach out on twitter: [@sirtimbly](https://twitter.com/sirtimbly).\n\n## Quick Demo App\n\n```ts\nconst frets = setup((f) =\u003e {\n  //... register MODELS\n  //... register VIEWS\n    //... inside your views, register ACTIONS and FIELDS\n});\n\n// customize the state calculation function that gets called before every re-render\nfrets.stateRenderer = (newProps: TodoListProps, oldProps: TodoListProps): TodoListProps =\u003e {\n  // add your derived state business logic here\n  return newProps\n};\n\n// mount it to the DOM\nfrets.mountTo(\"main\")\n\n```\nRead the [docs](https://sirtimbly.github.io/frets/) for more details about getting started!\n\n## What and Why?\n\nFRETS is a set of classes and interfaces to make it easy for you to write code that complies (mostly) with the SAM pattern. You can get all the reassurance of reliable code completion and type checking while still writing \"pure\" render functions. I think classes made up of functions are a perfectly valid way of giving developers the convenience of automatic code completion, and the other advantages of the Typescript tooling world. Making a developer remember all the variable names or massive copy-pasting is the enemy of clean bug-free code.\n\n\n## Functional Rendering with TypeScript Helper Classes\n\nRendering with a plain hyperscript function - like the `h()` function [provided by maquette](https://maquettejs.org/typedoc/index.html#h) is powerful - but most develoers aren't sadists - so they replace it with TSX, JSX or another template compiler. I usually wouldn't bet against HTML, but JSX has always just felt kinda annoying to me. It differs from HTML in a few important ways. And HTML isn't really that great for developers to begin with.\n\nAs a web developer I would recommend against any abstractions that pull us too far away from HTML/CSS. I've seen the pain of ASP.Net WebForms and Adobe Flex. Yet, here I am recommending developers use an abstraction that breaks fundamental rules about applying HTML and CSS to your site.\n\nI created the [FRETS-Styles-Generator](https://gitlab.com/FRETS/frets-styles-generator) tool to read a CSS file and turn it into a TS class that proxies the functionality of the maquette `h()` function. Normally you would pass your classnames to it like so `h(\"div.card.content-area\", [\"my content\"])`. The css selector is a big glaring ugly magic string, it's not refactorable or type checked. So with the generated class now you can express the above function like this `$.div.card.contentArea.h([\"my content\"])`. If you choose to run the generator against one of the [several \"atomic css\" libraries out there](https://css-tricks.com/lets-define-exactly-atomic-css/) then you get a whole bunch of __composable__ classes that let you specify your your UI looks, what it's nested structure is, and encourages thorough refactoring within your TypeScript. Bonus, the generator runs everything through PostCSS so you can split up your stylesheets in to modules that you can `@import` and make use of new CSS variable.\n\nBigger bonus, if you go into your stylesheets and start removing classes, the generator will rebuild the TypeScript class and your TS project will give you *build errors* if any of your CSS is now missing! That could make maintenance of app stylesheets a lot easier.\n\n## License\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fsirtimbly%2Ffrets.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fsirtimbly%2Ffrets?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsirtimbly%2Ffrets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsirtimbly%2Ffrets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsirtimbly%2Ffrets/lists"}