{"id":15099476,"url":"https://github.com/chnicoloso/lit-jsx","last_synced_at":"2025-03-15T23:15:11.411Z","repository":{"id":243724359,"uuid":"813270782","full_name":"chnicoloso/lit-jsx","owner":"chnicoloso","description":"A JSX runtime for LitElement","archived":false,"fork":false,"pushed_at":"2024-07-12T03:34:09.000Z","size":281,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-26T04:33:42.045Z","etag":null,"topics":["jsx","jsx-runtime","lit","lit-element","webcomponents","webcomponents-framework","webxr"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/chnicoloso.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-10T19:59:42.000Z","updated_at":"2025-02-03T21:16:59.000Z","dependencies_parsed_at":"2025-02-26T04:32:24.593Z","dependency_job_id":"8c4b0b94-59da-4067-ad69-eadaadd9acc6","html_url":"https://github.com/chnicoloso/lit-jsx","commit_stats":null,"previous_names":["chnicoloso/lit-jsx","chnicoloso/litjsx"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chnicoloso%2Flit-jsx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chnicoloso%2Flit-jsx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chnicoloso%2Flit-jsx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chnicoloso%2Flit-jsx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chnicoloso","download_url":"https://codeload.github.com/chnicoloso/lit-jsx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243801688,"owners_count":20350108,"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":["jsx","jsx-runtime","lit","lit-element","webcomponents","webcomponents-framework","webxr"],"created_at":"2024-09-25T17:21:28.409Z","updated_at":"2025-03-15T23:15:11.368Z","avatar_url":"https://github.com/chnicoloso.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lit-jsx\n\n`lit-jsx` is a library that provides a convenient way to build Lit components using JSX syntax. The only dependency for the library is `lit` itself.\n\n## Why?\nPersonal aesthetic preference: I am on board with everything about Lit but I cannot bring myself to write components using string templates.\n\n## Installation\nYou can install `lit-jsx` via npm:\n\n```bash\nnpm install @chnicoloso/lit-jsx\n```\n\n## Usage\nTo use `lit-jsx` simply import from `@chnicoloso/lit-jsx` whatever you would otherwise import from `lit`. You also need to configure your bundler or transpiler to use `lit-jsx` for processing JSX, instead of the default. For example in TypeScript you would add something like this to your `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"@chnicoloso/lit-jsx\",\n    \"types\": [ \"@chnicoloso/lit-jsx\" ]\n  }\n}\n```\nThere is a similar mechanism for Babel where you would add something like this to your `babel.config.js`:\n```json\n{\n    \"plugins\": [\n    [\n      \"@babel/plugin-transform-react-jsx\",\n      {\n        \"throwIfNamespace\": false,\n        \"runtime\": \"automatic\",\n        \"importSource\": \"@chnicoloso/lit-jsx\"\n      }\n    ]\n  ]\n}\n```\n\n### Example\n```typescript\nimport { LitElement, createRoot, customElement } from '@chnicoloso/lit-jsx';\n\n@customElement('my-app')\nclass App extends LitElement {\n    render() {\n        return (\n            \u003cbutton onClick={console.log}\u003e\n                Click Me!\n            \u003c/button\u003e\n        );\n    }\n}\n\nconst app = document.createElement('div');\ndocument.body.appendChild(app);\n\nconst root = createRoot(app);\nroot.render(new App());\n\n// Clean up.\nconst onUnload = () =\u003e {\n    window.removeEventListener('beforeunload', onUnload);\n    root.unmount();\n};\nwindow.addEventListener('beforeunload', onUnload);\n```\n\n### Running the Example\n```bash\ncd ./example\nnpm install\nnpm run watch\n```\nGo to https://localhost:8080/\n\n### Custom Elements\n\n`lit-jsx` allows you to customize what should be rendered for each tag name. For example, if you want JSX `\u003cbutton /\u003e` to result in `\u003cmy-custom-button /\u003e`, you would pass an override in your registry.\n\nHere's how you would do that:\n\n```typescript\nimport { assignElements, resetElements } from '@chnicoloso/lit-jsx';\n\n// Define your custom elements\nconst customElements = {\n  button: 'my-custom-button',\n  // Add more custom elements as needed\n};\n\n// Assign your custom elements\nassignElements(customElements);\n\n// Later, if you want to reset to the default elements\nresetElements();\n```\n#### And why in the world would I want to do _that_?\n\nThe original motivation behind this jsx-runtime was for a WebXR UI framework I was working on. In WebXR, any UI you make _has_ to be rendered using Canvas/webgl which is as fun as it sounds so I wanted to be able to define UI components using HTML syntax. My basic idea was to create webgl/three-js versions of each HTML element and then configure `lit-jsx` so that whenever the JSX called for say, a “button” to be rendered, the webgl equivalent would be rendered instead.\n\nI got a fair amount of it working including divs, images, text, overflow management, scroll bars, flex-box, border-radii, background colors, etc.\n\u003cdetails\u003e\n\u003csummary\u003eExpand to see the code for this \"component\"\u003c/summary\u003e\n\n```typescript\n/** @jsxImportSource src/lit-jsx */\n\nimport classNames from 'classnames';\nimport { createRoot, state, customElement, css } from 'src/canvas-elements';\nimport * as CanvasElements from 'src/canvas-elements';\n\n@customElement('my-scroller')\nclass HorizontalScroller extends CanvasElements.Component {\n\n    static styles = css`\n        .horizontal-scroller {\n            flex-direction: row;\n            width: 1px;\n            height: 1px;\n            align-items: center;\n            justify-content: flex-start;\n            border-radius: 0.02px;\n            overflow: scroll;\n        }\n\n        .box {\n            width: 0.3px;\n            height: 0.3px;\n            border-width: 0.04px;\n            border-radius: 0.02px;\n        }\n\n        .yellow { background-color: yellow; }\n        .green { background-color: green; }\n        .purple { background-color: purple; }\n        .red { background-color: darkred; }\n        .cyan { background-color: cyan; }\n        .blue { background-color: blue; }\n        .orange { background-color: orange; }\n    `;\n\n    render() {\n        return (\n            \u003cdiv className=\"horizontal-scroller cyan\"\u003e\n                \u003cdiv className=\"box purple\" /\u003e\n                \u003cdiv className=\"box green\" /\u003e\n                \u003cdiv className=\"box blue\" /\u003e\n                \u003cdiv className=\"box orange\" /\u003e\n                \u003cdiv className=\"box cyan\" /\u003e\n                \u003cdiv className=\"box red\" /\u003e\n                \u003cdiv className=\"box green\" /\u003e\n                \u003cdiv className=\"box purple\" /\u003e\n                \u003cdiv className=\"box blue\" /\u003e\n                \u003cdiv className=\"box red\" /\u003e\n            \u003c/div\u003e\n        );\n    }\n}\n\n@customElement('my-app')\nexport default class App extends CanvasElements.Component {\n\n    static styles = css`\n        * {\n            border-radius: 0.02px;\n        }\n\n        .root-container {\n            width: 100%;\n            height: 100%;\n            overflow: auto;\n            flex-direction: row;\n            align-items: center;\n            justify-content: space-between;\n        }\n\n        .padded-space-between-container {\n            flex-direction: row;\n            width: 1px;\n            height: 1px;\n            flex-wrap: wrap;\n            align-items: center;\n            justify-content: space-between;\n            padding: 0.2px;\n            border-radius: 0.02px;\n            background-opacity: 0.5;\n        }\n\n        .vertical-scroller {\n            flex-direction: column;\n            width: 1px;\n            height: 1px;\n            align-items: center;\n            justify-content: flex-start;\n            border-width: 0.04px;\n            border-radius: 0.02px;\n            overflow: scroll;\n            padding: 0.05px;\n        }\n\n        .text-container {\n            width: 1px;\n            height: 1px;\n            flex-direction: column;\n            align-items: flex-start;\n            justify-content: flex-start;\n            border-width: 0.04px;\n            border-radius: 0.02px;\n            overflow: auto;\n            font-size: 0.1px;\n            color: white;\n            overflow-wrap: break-word;\n        }\n\n        .small-box {\n            width: 0.2px;\n            height: 0.2px;\n        }\n\n        .medium-box {\n            width: 1px;\n            height: 1px;\n            flex-direction: column;\n            align-items: flex-start;\n            justify-content: flex-start;\n            border-width: 0.04px;\n            border-radius: 0.02px;\n        }\n\n        .image {\n            border-width: 0.04px;\n            border-radius: 0.02px;\n        }\n\n        .yellow { background-color: yellow; }\n        .green { background-color: green; }\n        .purple { background-color: purple; }\n        .red { background-color: darkred; }\n        .cyan { background-color: cyan; }\n        .blue { background-color: blue; }\n        .orange { background-color: orange; }\n    `;\n\n    connectedCallback(): void {\n        super.connectedCallback();\n        window.addEventListener('click', this._updateWidth);\n        window.addEventListener('keyup', this._updateText);\n    }\n\n    disconnectedCallback(): void {\n        super.disconnectedCallback();\n        window.removeEventListener('click', this._updateWidth);\n        window.removeEventListener('keyup', this._updateText);\n    }\n\n    @state()\n    width = 0.2;\n\n    @state()\n    text = '';\n\n    _updateWidth = () =\u003e {\n        this.width += 0.1;\n    }\n\n    _updateText = (keyEvent) =\u003e {\n        this.text += keyEvent.key;\n    }\n\n    get verticalScroller() {\n        const colors = [ 'purple', 'green', 'blue', 'orange', 'cyan', 'red', 'green', 'purple', 'blue', 'red' ];\n        return (\n            \u003cdiv className=\"vertical-scroller orange\"\u003e\n                {colors.map(color =\u003e \u003cdiv className={classNames('small-box', color)} /\u003e)}\n            \u003c/div\u003e\n        );\n    }\n\n    render() {\n        return (\n            \u003cdiv className=\"root-container blue\"\u003e\n                \u003cdiv className=\"padded-space-between-container green\"\u003e\n                    \u003cdiv className=\"small-box purple\" /\u003e\n                    \u003cdiv className=\"small-box expandable purple\" /\u003e\n                \u003c/div\u003e\n                {this.verticalScroller}\n                \u003cimg\n                    width={2}\n                    height={2}\n                    className=\"image yellow\"\n                    src=\"https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg\"\n                /\u003e\n                \u003cimg\n                    width={1}\n                    height={1}\n                    className=\"image green\"\n                    src=\"https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png\"\n                /\u003e\n                \u003cdiv className=\"text-container red\"\u003e\n                    1. ByeasfkndksnfmfdsamfksafksdByeasfkndksnfmfdsamfksaf\n                    \u003cdiv className=\"small-box purple\" /\u003e\n                    {`2. Type here: ${this.text}`}\n                \u003c/div\u003e\n                \u003cHorizontalScroller /\u003e\n                \u003cdiv className={classNames('medium-box', {\n                    purple: this.width \u003c 0.5,\n                    yellow: this.width \u003e= 0.5\n                })}/\u003e\n            \u003c/div\u003e\n        );\n    }\n}\n\nconst root = createRoot(document.getElementById('view'));\nroot.render(new App());\n```\n\u003c/details\u003e\n\n![Jun-11-2024 20-09-42](https://github.com/chnicoloso/lit-jsx/assets/9637975/0e6f39a4-2a6d-48b4-8717-fd974f5feb71)\n\n\nI also managed to embed these \"HTML\" components onto ThreeJS Scenes and I made some progress towards a \"composer\" based on [Exokit](https://github.com/exokitxr/exokit) that would allow multiple independent \"plugin\" components to be combined onto the same \"host\" scene\n![Jun-11-2024 20-26-21](https://github.com/chnicoloso/lit-jsx/assets/9637975/dfea6932-7746-4db9-a00c-2a9e5cc2c417)\n\nUltimately however, I decided that this simply cannot not be future of the [Immersive Web](https://immersiveweb.dev/) and that an _immersive_ (not just mobile) version of something like [DOM Overlays](https://www.w3.org/TR/webxr-dom-overlays-1/) is needed in the WebXR standard - maybe taking advantage of the existing CSS-transform's 3D capabilities, as shown by ThreeJS's [CSS3DRenderer](https://threejs.org/docs/#examples/en/renderers/CSS3DRenderer).\n![Jun-11-2024 20-53-40](https://github.com/chnicoloso/lit-jsx/assets/9637975/2f3a10d5-07cf-4993-876b-00c6d0e8609e)\n\nGood times though.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchnicoloso%2Flit-jsx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchnicoloso%2Flit-jsx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchnicoloso%2Flit-jsx/lists"}