{"id":20513899,"url":"https://github.com/webreflection/p-cool","last_synced_at":"2025-10-31T03:34:40.164Z","repository":{"id":57317440,"uuid":"392968861","full_name":"WebReflection/p-cool","owner":"WebReflection","description":"Pretty Cool Elements","archived":false,"fork":false,"pushed_at":"2022-06-09T07:25:34.000Z","size":44,"stargazers_count":37,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-10T02:44:12.196Z","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}},"created_at":"2021-08-05T08:42:03.000Z","updated_at":"2024-09-02T13:18:01.000Z","dependencies_parsed_at":"2022-08-25T21:11:53.927Z","dependency_job_id":null,"html_url":"https://github.com/WebReflection/p-cool","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fp-cool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fp-cool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fp-cool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fp-cool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WebReflection","download_url":"https://codeload.github.com/WebReflection/p-cool/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248799931,"owners_count":21163403,"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-11-15T21:13:45.126Z","updated_at":"2025-10-31T03:34:35.146Z","avatar_url":"https://github.com/WebReflection.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pretty Cool Elements\n\n\u003csup\u003e**Social Media Photo by [Jamison McAndie](https://unsplash.com/@jamomca) on [Unsplash](https://unsplash.com/)**\u003c/sup\u003e\n\n\nThis module is a follow up of [this Medium post](https://webreflection.medium.com/about-web-components-cc3e8b4035b0), and it provides element mixins/behaviors, through class names, without names clashing.\n\n\n### Features\n\n  * it addresses every single point touched in the Medium's post:\n    * no name clashing\n    * multiple mixins/behaviors attached/detached at any time\n    * native Custom Elements builtin callbacks, associated to mixins/behaviors\n  * it's **S**erver **S**ide **R**endering compatible out of the box\n  * it uses all the DOM primitives without needing an extra attribute (bloat-free layouts)\n  * it's semantically bound with element's view (their classes and their dedicated style)\n  * it's graceful enchancement out of the box, based on builtin extends\n  * it provides a robust polyfilled version through [vanilla-elements](https://github.com/WebReflection/vanilla-elements#readme)\n\n\n#### Example\n\n```js\nimport {define} from 'p-cool';\n\ndefine('my-div', {\n\n  // to know when a behavior is attached or detached via class\n  attachedCallback(element) {},\n  detachedCallback(element) {}, // see ## About Callbacks\n\n  // to observe connected/disconnected lifecycle\n  connectedCallback(element) {},\n  disconnectedCallback(element) {},\n\n  // to observe specific attributes (omit to observe them all)\n  observedAttributes: ['some-attribute'],\n\n  // to know when observed attributes changed\n  attributeChangedCallback(element, name, oldValue, newValue) {},\n});\n```\n\n```html\n\u003cdiv is=\"p-cool-div\" class=\"my-div\" some-attribute=\"ok\"\u003e\n  Hello Behaviors 👋\n\u003c/div\u003e\n```\n\n## About PCool\n\nWith native Custom Elements, we need to reserve a single name in a shared global registry to pass through the upgrade, and callbacks, mechanism.\n\nWith `p-cool` elements, the registry is pre-populated with [vanilla-elements](https://github.com/WebReflection/vanilla-elements#readme) extends through a `p-cool-*` prefix, so that `\u003cdiv is=\"p-cool-div\"\u003e`, and `\u003cp is=\"p-cool-p\"\u003e`, or `\u003cmain is=\"p-cool-main\"\u003e` are all valid, already registered, builtin extends, that brings mixins/behaviors to any element, and through their class name, as long as one, or mixin, is defined/attached, through the `define(name, mixin)` module's export.\n\n```html\n\u003c!doctype html\u003e\n\u003cscript type=\"module\"\u003e\nimport {define} from '//unpkg.com/p-cool';\n\nimport Hero from './mixins/hero.js';\ndefine('hero', Hero);\n\nimport Bottom from './mixins/bottom.js';\nimport AutoHide from './mixins/auto-hide.js';\ndefine('bottom', Bottom);\ndefine('auto-hide', AutoHide);\n\u003c/script\u003e\n\u003cstyle\u003e\nmain { /* ... */ }\n.hero { /* ... */ }\n.bottom { /* ... */ }\n\u003c/style\u003e\n\u003cbody is=\"p-cool-body\" class=\"hero\"\u003e\n  \u003cmain\u003eHero\u003c/main\u003e\n  \u003cfooter is=\"p-cool-footer\" class=\"bottom auto-hide\"\u003e\n    ...\n  \u003c/footer\u003e\n\u003c/body\u003e\n```\n\nTo implement an *element extend*, the `\u003cp-cool\u003e` *Custom Element* is registered too, so that a page could be defined by non-builtin extends, with mixins/behaviors attached when, and if, needed.\n\n```html\n\u003c!doctype html\u003e\n\u003cscript type=\"module\"\u003e\nimport {define} from '//unpkg.com/p-cool';\n\nimport Hero from './mixins/hero.js';\ndefine('hero', Hero);\n\nimport Bottom from './mixins/bottom.js';\nimport AutoHide from './mixins/auto-hide.js';\ndefine('bottom', Bottom);\ndefine('auto-hide', AutoHide);\n\u003c/script\u003e\n\u003cbody\u003e\n  \u003cp-cool class=\"hero\"\u003e\u003c/p-cool\u003e\n  \u003cp-cool class=\"bottom auto-hide\"\u003e\n    ...\n  \u003c/p-cool\u003e\n\u003c/body\u003e\n```\n\n\n## About Callbacks\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eattachedCallback\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThis callback is granted to be invoked only *once*, and *before* any other callback, whenever a mixin/behavior is attached through the element's class, somehow simulating what a `constructor` would do with Custom Elements.\n\nThis callback is ideal to add related event listeners, setup an element for the specific mixin/behavior, and so on.\n\nPlease note that if a mixin/behavior is detached, and then re-attached, this callback *will* be invoked again.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eattributeChangedCallback\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nIf any `observedAttributes` is specified, or if there is an `attributeChangedCallback`, this is invoked every time observed attributes change.\n\nLike it is for *Custom Elements*, this callback is invoked, after a mixin/behavior is attached, hence *after* `attachedCallback`, but *before* `connectedCallback`.\n\nThis callback is also invoked during the element lifecycle, whenever observed attributes change, providing the `oldValue` and the `newValue`.\n\nBoth values are `null` if there was not attribute, or if the attribute got removed, replicating the native *Custom Element* behavior.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003econnectedCallback\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThis callback is granted to be invoked *after* an element gets a new mixin/behavior, if the element is already live, and every other time the element gets moved or re-appended on the DOM, exactly like it is for native *Custom Elements*.\n\nPlease note that when a mixin/behavior is attached, and there are observed attributes, this callback will be invoked *after* `attributeChangedCallback`.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003edisconnectedCallback\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThis callback is granted to be invoked when an element gets removed from the DOM, and it would never trigger if the `connectedCallback` didn't happen already.\n\nBoth callbacks are the ideal place to attach, on *connected*, and remove, on *disconnected*, timers, animations, or idle related callbacks, as even when elements get trashed, both callbacks are granted to be executed, and in the right order of events.\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003edetachedCallback\u003c/strong\u003e\u003c/summary\u003e\n  \u003cdiv\u003e\n\nThis callback is **not granted to be invoked** if an element get trashed, but it's granted to be invoked *after* `disconnectedCallback`, if a mixin/behavior is removed from an element.\n\nPlease note that this callback is *not* really useful for elements that might be, or may not be, trashed, because there is no way to use a *FinalizationRegistry* and pass along the `element`, but it's very hando for those elements that never leave the DOM, but might change, over time, their classes, hence their mixins/behaviors.\n\n```js\nimport {define} from 'p-cool';\n\ndefine('mixin', {\n  attachedCallback(element) {\n    console.log('mixin attached');\n  },\n  detachedCallback(element) {\n    console.log('mixin detached');\n  }\n});\n\n// example\ndocument.body.innerHTML = `\n  \u003cdiv id=\"first\" class=\"mixin\"\u003eFirst\u003c/div\u003e\n  \u003cdiv id=\"second\" class=\"mixin\"\u003eSecond\u003c/div\u003e\n`;\n// logs \"mixin attached\" twice\n\n// will **not** \"mixin detached\"\nfirst.remove();\n\n// it **will** log \"mixin detached\"\nsecond.classList.remove('mixin');\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\n## About Exports\n\nThis module offers the following exports:\n\n  * `p-cool` with a `define(name, mixin)` export that *does not polyfill Safari*\n  * `p-cool/min` with a minified `define(name, mixin)` export that *does not polyfill Safari*\n  * `p-cool/poly` with a minified `define(name, mixin)` export that also *does polyfill Safari*\n  * `p-cool/behaviors` with the internally used `define` and `behaviors` exports, plus constants, useful to potentially create other libraries or utilities on top of the same logic\n\nThe `https://unpkg.com/p-cool` points at the minified `/poly` variant, useful to quickly test, or develop, with this module.\n\n\n## Compatibility\n\nEvery ES2015+ compatible browser out of the box, including Safari/WebKit based browsers in the *poly* version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebreflection%2Fp-cool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebreflection%2Fp-cool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebreflection%2Fp-cool/lists"}