{"id":23272549,"url":"https://github.com/sister-software/my-app","last_synced_at":"2025-10-11T22:36:35.788Z","repository":{"id":34078732,"uuid":"160718588","full_name":"sister-software/my-app","owner":"sister-software","description":"Fast and simple web apps without build tools. (Work in progress)","archived":false,"fork":false,"pushed_at":"2022-02-12T14:39:00.000Z","size":111,"stargazers_count":3,"open_issues_count":10,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-18T12:19:19.266Z","etag":null,"topics":["custom-elements","unframework","web-components"],"latest_commit_sha":null,"homepage":"https://my-app.js.org","language":"TypeScript","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/sister-software.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-12-06T18:58:57.000Z","updated_at":"2022-10-04T10:24:12.000Z","dependencies_parsed_at":"2022-08-08T00:00:43.190Z","dependency_job_id":null,"html_url":"https://github.com/sister-software/my-app","commit_stats":null,"previous_names":["nirrius/my-app"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sister-software%2Fmy-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sister-software%2Fmy-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sister-software%2Fmy-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sister-software%2Fmy-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sister-software","download_url":"https://codeload.github.com/sister-software/my-app/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247463938,"owners_count":20942951,"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":["custom-elements","unframework","web-components"],"created_at":"2024-12-19T19:17:51.643Z","updated_at":"2025-10-11T22:36:30.746Z","avatar_url":"https://github.com/sister-software.png","language":"TypeScript","readme":"# `\u003cmy-app /\u003e`\n\n_Progressive Web Components_\n\n**Unreleased work in progress**\n\nNo Babel. No Webpack. Progressive Web Components let you build full web apps native custom HTML elements.\n\n```css\n:host {\n  --primary-color-base: #ff0000;\n}\n```\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003clink src=\"./theme.css\" type=\"text/my-app-theme\" /\u003e\n    \u003c!-- Add your modules as script tags. --\u003e\n    \u003cscript src=\"https://unpkg.com/progressive-web-components/loader.js\"\u003e\u003c/script\u003e\n    \u003cscript type=\"module\" src=\"https://unpkg.com/@progressive-web-components/web-component.js\"\u003e\u003c/script\u003e\n\n    \u003c!-- Declare your components with minimal JavaScript --\u003e\n\n    \u003cweb-component tag-name=\"human-time\"\u003e\n      \u003cobserved-attribute name=\"date\" type=\"number\" required\u003e\u003c/observed-attribute\u003e\n\n      \u003ctemplate\u003e\n        \u003cspan title=\"${this.observedAttributes.date}\"\u003e${this.humanizedTime()}\u003c/span\u003e\n      \u003c/template\u003e\n\n      \u003cscript type=\"module\"\u003e\n        import moment from './node_modules/moment/moment.js'\n\n        this.humanizedTime = () =\u003e {\n          return moment.fromNow(this.observedAttributes.date)\n        }\n\n        this.addEventListener('afterInsert', () =\u003e {\n          this.interval = setInterval(() =\u003e {\n            this.requestTemplateUpdate()\n          }, 1000 * 60)\n        })\n\n        this.addEventListener('beforeRemove', () =\u003e {\n          clearInterval(this.interval)\n        })\n      \u003c/script\u003e\n    \u003c/web-component\u003e\n\n    \u003c!-- ...Or reference components in separate files --\u003e\n\n    \u003cweb-component src=\"/components/human-time.component.html\"\u003e\u003c/web-component\u003e\n\n    \u003c!-- ...Or declare them as JavaScript classes for total control --\u003e\n\n    \u003cscript type=\"module\"\u003e\n      import WebComponent from 'node_modules/@progressive-web-components/web-component.js'\n      import moment from './node_modules/moment/moment.js'\n\n      class HumanTime extends WebComponent {\n        static tagName = 'human-time'\n\n        static observedAttributes = {\n          date: {\n            type: Date,\n            required: true\n          }\n        }\n\n        onafterinsert() {\n          this.interval = setInterval(() =\u003e {\n            this.requestTemplateUpdate()\n          }, 1000 * 60)\n        }\n\n        onbeforeremove() {\n          clearInterval(this.interval)\n        }\n\n        humanizedTime() {\n          return moment.fromNow(this.observedAttributes.date)\n        }\n\n        template(html) {\n          return html`\n            \u003cspan title=\"${this.observedAttributes.date}\"\u003e${this.humanizedTime()}\u003c/span\u003e\n          `\n        }\n      }\n    \u003c/script\u003e\n\n    \u003cscript\u003e\n      // ...Then natively import them — without a build step.\n      import MyApp from '/vendor/my-app.js'\n\n      // Create native custom elements.\n      MyApp.createElement('todo-list-page', {\n        // Element scoped styles.\n        styles(css) {\n          css`\n            :host {\n              border: 1px dashed black;\n            }\n            .todos {\n              border: 1px solid black;\n            }\n          `\n        },\n\n        methods: {\n          markTodoComplete(event) {\n            const { todoId } = event.target.dataset\n\n            this.parentApp.sharable.markTodoComplete(todoId)\n          }\n        },\n\n        render(html) {\n          const { todos } = this.parentApp.sharable\n\n          return html`\n            \u003cdiv\u003e\n              \u003ch1\u003eTodo List\u003c/h1\u003e\n\n              \u003cul class=\"todos\"\u003e\n                ${\n                  todos.entries.map(\n                    todo =\u003e html`\n                      \u003cli onClick=${this.markTodoComplete} data-todo-id=${todo.id}\u003e${todo.title}\u003c/li\u003e\n                    `\n                  )\n                }\n              \u003c/ul\u003e\n            \u003c/div\u003e\n          `\n        }\n      })\n\n      const myApp = new MyApp({\n        // Share global data with child nodes.\n        sharable: {\n          todos: {\n            entries: [{ id: 'abc123', title: 'Try MyApp', complete: true }],\n            markTodoComplete(todoId) {\n              const todo = this.todos.find(todo =\u003e todo.id === todoId)\n              todo.complete = true\n            }\n          }\n        },\n        routes: {\n          // Map routes to custom elements.\n          '/': TodoListPage,\n          login: LoginPage\n        }\n      })\n\n      document.body.appendChild(myApp)\n    \u003c/script\u003e\n  \u003c/head\u003e\n\n  \u003cbody\u003e\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Theming (WIP)\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003clink rel=\"stylesheet\" type=\"text/css\" media=\"screen\" href=\"main.css\" /\u003e\n  \u003cscript type=\"module\" src=\"material-button.js\"\u003e\u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cmy-theme\u003e\n    \u003cstyle\u003e\n      /* Global theme variables. */\n      :root {\n        --font-size: 16px;\n        --color: #111;\n      }\n\n      /* Element overrides */\n      material-button::part(text) {\n        font-style: italic;\n      }\n    \u003c/style\u003e\n\n    \u003cmaterial-button\u003eClick me!\u003c/material-button\u003e\n  \u003cmy-theme\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsister-software%2Fmy-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsister-software%2Fmy-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsister-software%2Fmy-app/lists"}