{"id":13560405,"url":"https://github.com/WebReflection/uce","last_synced_at":"2025-04-03T15:32:17.361Z","repository":{"id":48280920,"uuid":"242879256","full_name":"WebReflection/uce","owner":"WebReflection","description":"µhtml based Custom Elements","archived":false,"fork":false,"pushed_at":"2024-01-26T17:43:34.000Z","size":448,"stargazers_count":199,"open_issues_count":0,"forks_count":16,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-29T20:09:41.408Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WebReflection.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}},"created_at":"2020-02-25T01:10:00.000Z","updated_at":"2025-03-11T09:04:14.000Z","dependencies_parsed_at":"2022-09-05T06:20:46.271Z","dependency_job_id":"a06a7da2-c6d5-4be5-b3b5-78e7435e0a14","html_url":"https://github.com/WebReflection/uce","commit_stats":{"total_commits":229,"total_committers":5,"mean_commits":45.8,"dds":0.05240174672489084,"last_synced_commit":"b4aaef9bda86efdb0e8839b6e2ba32c1a0621bc1"},"previous_names":[],"tags_count":94,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fuce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WebReflection","download_url":"https://codeload.github.com/WebReflection/uce/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247028132,"owners_count":20871654,"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":[],"created_at":"2024-08-01T13:00:43.447Z","updated_at":"2025-04-03T15:32:12.347Z","avatar_url":"https://github.com/WebReflection.png","language":"JavaScript","readme":"# \u003cem\u003eµ\u003c/em\u003ece\n\n[![Downloads](https://img.shields.io/npm/dm/uce.svg)](https://www.npmjs.com/package/uce) [![Build Status](https://travis-ci.com/WebReflection/uce.svg?branch=master)](https://travis-ci.com/WebReflection/uce) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/uce/badge.svg?branch=master)](https://coveralls.io/github/WebReflection/uce?branch=master)\n\n![windflower](./uce-head.jpg)\n\n\u003csup\u003e**Social Media Photo by [Dawid Zawiła](https://unsplash.com/@davealmine) on [Unsplash](https://unsplash.com/)**\u003c/sup\u003e\n\n**[µhtml](https://github.com/WebReflection/uhtml#readme)** based Custom Elements.\n\n\n### 📣 Community Announcement\n\nPlease ask questions in the [dedicated discussions repository](https://github.com/WebReflection/discussions), to help the community around this project grow ♥\n\n---\n\n\n## API Overview\n\n_\u003cem\u003eµ\u003c/em\u003ece_ exports `render`, `html`, and `svg`, from _\u003cem\u003eµ\u003c/em\u003ehtml_, plus its own way to `define` components.\n\nIn version *1.2*, it exports also a [plain-tag](https://github.com/WebReflection/plain-tag#readme) named `css`, useful to trigger _CSS_ minifiers.\n\nCheck out the [test page](https://webreflection.github.io/uce/test/) or this [code pen playground](https://codepen.io/WebReflection/pen/MWwJpWx?editors=0010).\n\n```js\n// list of all exports\nimport {define, render, html, svg, css} from 'uce';\n\ndefine('my-component', {\n\n  // if specified, it can extend built-ins too.\n  // by default it's 'element', as HTMLElement\n  extends: 'div',\n\n  // if specified, it injects once per class definition\n  // a global \u003cstyle\u003e element in the document \u003chead\u003e, but\n  // *not* in the shadowRoot. You can render style just fine\n  // within the render, if shadow DOM is desired.\n  // For the one-off global case, this method will be invoked with\n  // a selector like `div[is=\"my-component\"]` or `some-component`\n  style: selector =\u003e css`${selector} {\n    font-weight: bold;\n  }`,\n\n  // if specified, it's like the constructor but\n  // it's granted to be invoked *only once* on bootstrap\n  // and *always* before connected/attributeChanged/props\n  init() {\n    // µhtml is provided automatically via this.html\n    // it will populate the shadow root, even if closed\n    // or simply the node, if no attachShadow is defined\n    this.html`\u003ch1\u003eHello 👋 µce\u003c/h1\u003e`;\n    // a default props access example\n    // \u003cmy-ce name=\"ag\" /\u003e\n    console.log(this.props.name); // \"ag\"\n  },\n\n  // if there is a render method, and no `init`,\n  // this method will be invoked automatically on bootstrap.\n  // element.render(), if present, is also invoked automatically\n  // when `props` are defined as accessors, and one of these is\n  // set during some outer component render()\n  render() {\n    this.html`\u003ch1\u003eHello again!\u003c/h1\u003e`;\n  },\n\n  // by default, props resolves all attributes by name\n  // const {prop} = this.props; will be an alias for\n  // this.getAttribute('prop') operation,\n  // but it can simulate what React props do,\n  // meaning that if it's defined as object,\n  // all properties will trigger automatically\n  // a render() call, if there is a render,\n  // and properties are set as accessor, so that\n  // the syntax to trigger these is .prop=${value}\n  // as opposite of the default prop=${value}\n  // which is observable, but it can hold only strings.\n  // props: {prop: value} will make this.prop work.\n  // If you don't want any of this machinery around props\n  // you can opt out by defining it as null.\n  // Bear in mind, the way to pass props as accessors,\n  // is by prefixing the attribute via `.`, that is:\n  // this.html`\u003cmy-comp .prop=${value}/\u003e`;\n  props: null,\n\n  // if present, all names will be automatically bound to the element\n  // right before initialization (el.method = el.method.bind(el))\n  // this allows usage of methods instead of `this` for inner components\n  bound: ['method'],\n\n  // if specified, it renders within its Shadow DOM\n  // compatible with both open and closed modes\n  attachShadow: {mode: 'closed'},\n\n  // if specified, observe the list of attributes\n  observedAttributes: ['test'],\n\n  // if specified, will be notified per each\n  // observed attribute change\n  attributeChanged(name, oldValue, newValue){},\n\n  // if specified, will be invoked when the node\n  // is either appended live, or removed\n  connected() {},\n  disconnected() {},\n\n  // events are automatically attached, as long\n  // as they start with the `on` prefix\n  // the context is *always* the component,\n  // you'll never need to bind a method here\n  onClick(event) {\n    console.log(this); // always the current Custom Element\n  },\n\n  // if specified with `on` prefix and `Options` suffix,\n  // allows adding the listener with a proper third argument\n  onClickOptions: {once: true}, // or true, or default false\n\n  // any other method, property, or getter/setter will be\n  // properly configured in the defined class prototype\n  get test() { return Math.random(); },\n\n  set test(value) { console.log(value); },\n\n  sharedData: [1, 2, 3],\n\n  method() {\n    return this.test;\n  }\n\n});\n```\n\n## F.A.Q.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhich polyfill should I use?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThe [@ungap/custom-elements](https://github.com/ungap/custom-elements#readme) is the recommended polyfill to grant every Custom Elements V1 feature is available in every browser.\n\nHowever, if no builtin extend is used, but legacy needs to be supported, including [@webreflection/custom-elements-no-builtin](https://github.com/WebReflection/custom-elements-no-builtin#readme) on top of the page should patch [IE 11 and other legacy browsers](https://github.com/ungap/custom-elements#compatibility).\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eHow to avoid bundling µce per each component?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThis module reserves, in the Custom Elements Registry a `uce-lib` class, which only purpose is to provide all exports as static getters.\n\n```js\n// whenever uce library is loaded\ncustomElements\n  .whenDefined('uce-lib')\n  .then(({define, render, html, svg} = customElements.get('uce-lib')) =\u003e {\n    // that's it: ready to go 🎉\n    define('my-component', {\n      init() {\n        console.log('this is awesome!');\n      }\n    });\n  }\n);\n```\n\n\u003cstrong\u003eUsing a helper\u003c/strong\u003e\n\n\"_There's a module for that_\", it's called [once-defined](https://github.com/WebReflection/once-defined#readme):\n\n```js\nimport when from 'once-defined';\n\nwhen('uce-lib').then(({define, render, html, svg}) =\u003e {\n  // define your Custom Element\n});\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWithout classes, how does one define private properties?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nPrivate properties can be created via a _WeakMap_, which is indeed how _Babel_ transforms these anyway.\n\n```js\nconst privates = new WeakMap;\ndefine('ce-with-privates', {\n  init() {\n    // define these once\n    privates.set(this, {test: 1, other: '2'});\n  },\n  method() {\n    // and use it anywhere you need them\n    const {test, other} = privates.get(this);\n    console.log(test, other);\n  }\n});\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWithout classes, how does one extend other components?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThere are at least two ways to extend an _uce_ component:\n\n  * define via _uce_ your base component, and use `extends: \"base-comp-name\"` to extend it (built-ins supported!)\n  * use one or more mixin through object literals\n\nObject literals have indeed been used as mixin for a very long time, and the pattern with _uce_ would be very similar.\n\nThe only warning is that `Object.assign`, as well as object `{...spread}`, lose getters and setters in the process, so that if you want to extend more complex components, you should consider using [assignProperties](https://github.com/WebReflection/assign-properties#readme), or a similar helper.\n\n```js\nimport $ from 'assign-properties';\nconst mixin = (...components) =\u003e $({}, ...components);\n\n// a component literal definition\nconst NamedElement = {\n  get name () { return this.tagName; }\n};\n\n// a generic NamedElement mixin\nconst FirstComponent = mixin(NamedElement, {\n  method() {\n    console.log(this.name);\n  }\n});\n\n// define it via the FirstComponent mixin\ndefine('first-component', FirstComponent);\n\n// define it via mixin\ndefine('first-component', mixin(FirstComponent, {\n  otherThing() {}\n}));\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eHow different is \u003cem\u003eµce\u003c/em\u003e from others?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nI have written a gist that compares [uce vs lit-element](https://gist.github.com/WebReflection/ae3451c17c5e882bbc7f0714c14eefcd), so that most obvious differences are highlighted, but basically *uce* provides pretty much everything other libraries provide and vice-versa, and choosing one or another should be driven by personal taste and style, as long as most relevant differences are clear.\n\nThat is: *uce* is neither superior nor inferior to others, it tries to be as simple and concise as possible, and it has great pontentials when used via [uce-template](https://github.com/WebReflection/uce-template#readme) too.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhat happened between 1.2 and 1.5?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nA wrong `npm publish` happened, as `1.5.0` has been pushed for no reason between 0.5 an 0.6, so that latest was picking up actually an older version of the library.\n\nMy apologies.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eWhat's new in v1.2?\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nSo far, the only missing utility for *non* Shadow DOM cases, is a way to define *once* a generic *style* associated with a component, which is why the special `style: (selector) =\u003e css` property has been added, so that any component can automatically define any specific style, using the `selector` to confine inner nodes directives.\n\nThe `css` export is a plain template literal tag, which is completely optional, but it might help minifiers, or [rollup plugins](https://github.com/asyncLiz/rollup-plugin-minify-html-literals), to minify that code too.\n\n```js\n// note: the css import is optional\nimport {define, css} from 'uce';\n\ndefine('very-important', {\n  style: sel =\u003e css`\n    ${sel} {\n      font-weight: bold;\n      text-transform: uppercase;\n    }\n    ${sel}:hover {\n      font-size: 2rem;\n    }\n  `\n});\n```\n\nIf the element doesn't extend a built-in, the received `sel`, as _selector_, will simply be its name, otherwise it'll be the built-in name with its `[is=\"...\"]` attribute.\n\n**Please note** the `style` won't interfere, or be attached anyhow, with the regular `element.style` or `this.style`, within a method, which is actually why I've chosen that name, so it's clear it's about the generic class/component style, and not its property.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n","funding_links":[],"categories":["Libraries for building web components","JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWebReflection%2Fuce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FWebReflection%2Fuce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWebReflection%2Fuce/lists"}