{"id":15434515,"url":"https://github.com/paperstrike/stylingadvancedhtmlelement","last_synced_at":"2026-05-04T02:34:30.559Z","repository":{"id":103425894,"uuid":"285630893","full_name":"PaperStrike/StylingAdvancedHTMLElement","owner":"PaperStrike","description":"Dynamically creating custom elements without the fear of FOUC.","archived":false,"fork":false,"pushed_at":"2020-08-07T12:38:22.000Z","size":5,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-18T08:53:42.988Z","etag":null,"topics":["custom-elements","fouc","web-components"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/PaperStrike.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":"2020-08-06T17:27:11.000Z","updated_at":"2020-08-07T12:38:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"2bab29e1-d279-4602-bd99-15333ec5e19a","html_url":"https://github.com/PaperStrike/StylingAdvancedHTMLElement","commit_stats":{"total_commits":3,"total_committers":1,"mean_commits":3.0,"dds":0.0,"last_synced_commit":"1ec4d74296846c47b02a57142f9a2d9a75c554a9"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/PaperStrike/StylingAdvancedHTMLElement","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2FStylingAdvancedHTMLElement","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2FStylingAdvancedHTMLElement/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2FStylingAdvancedHTMLElement/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2FStylingAdvancedHTMLElement/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PaperStrike","download_url":"https://codeload.github.com/PaperStrike/StylingAdvancedHTMLElement/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2FStylingAdvancedHTMLElement/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32592694,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"online","status_checked_at":"2026-05-04T02:00:06.625Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","fouc","web-components"],"created_at":"2024-10-01T18:39:47.506Z","updated_at":"2026-05-04T02:34:30.541Z","avatar_url":"https://github.com/PaperStrike.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dynamical Custom Element Without FOUC\n\nIt has long been a problem that you have to either write css **inside** js code or import them from external file and bear a [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) (flash of unstyled content) when **dynamically creating custom elements**.\n\n\u003e **PS:** I found the issue by googling it for 10 minutes, so don't expect me to be responsible for the authenticity of the 'either...or...' sentence.\n\nbut there is a way.\n\n## Idea\n\nThe idea to the problem is almost the same as to solve FOUC of other kinds by operating related element: **hide before done**.\n\n### 1. Write a defalut style for the unstyled element.\n\nThe prefered CSS is like:\n\n```css\n:host {\n  position: absolute;\n  visibility: hidden;\n}\n```\n\n- `absolute` makes the element removed from the normal document flow, so that it won't occupy the space it should not.\n- Hide the element before style is loaded.\n\n**Q ?** Why you would like to remove my element from the normal document flow?\n\n**A !** Not liking FOUC of some kinds, which affects the whole page, the FOUC of custom elements only affects the element **itself**. So the solution to them has a little difference, too. Normal FOUC may be solved by hide the whole page before CSS is ready, while custom-element-FOUC **should not** do a such thing. In this case, keep the element in the normal flow may **occupy the wrong place**, causing **twice** \"FOOC\" (flash of other content). (Remember we are dynamically adding elements.)\n\n**Q ?** Why don't you use `display: none` directly? It's pretty clean.\n\n**A !** Simply set `display` to `none` will surely remove the element from the normal document flow and hide the whole thing, but browsers today [may not load contents of it and its descendants as well](https://stackoverflow.com/questions/12158540/does-displaynone-prevent-an-image-from-loading), which means images might **not be ready** for the next step, appearing. So I suggest the combination of `visibility` and `position`.\n\n### 2. Apply the style when ready.\n\nNow it's done. Looks simple, like many answers on Stack Overflow before, right? So, it's just adapting a **Custom Element** version:\n\n## Usage\n\nCopy the *sample code* below or download [`export.js`](https://github.com/PaperFlu/StylingAdvancedHTMLElement/raw/master/export.js).\n\n```javascript\nclass Style {\n  status = 'unloaded'; // unloaded, loading, complete.\n  url = '';\n  text = '';\n  applyQueue = [];\n\n  constructor(url) {\n    this.url = url;\n\n    this.update();\n  }\n\n  async update() {\n    if (this.status === 'loading') return;\n    this.status = 'loading';\n\n    await fetch(this.url).then(r =\u003e r.text()).then((text) =\u003e {\n      this.text = text;\n      this.status = 'complete';\n\n      for (const ele of this.applyQueue) {\n        ele.styleEle.textContent = text;\n      };\n    }).catch((error) =\u003e {\n      this.status = 'unloaded';\n      throw error;\n    });\n  }\n};\n\nclass StylingAdvancedHTMLElement extends HTMLElement {\n  static styleFileUrl;\n\n  static initialStyleText = `\n    :host {\n      position: absolute !important;\n      visibility: hidden !important;\n    }\n  `;\n\n  static styleList = [];\n\n  styleEle = document.createElement('style');\n\n  constructor() {\n    super();\n\n    const shadow = this.attachShadow({\n      mode: 'open',\n    });\n\n    const {\n      styleFileUrl,\n      initialStyleText,\n      styleList,\n    } = this.constructor;\n\n    let style = styleList.find(style =\u003e styleFileUrl === style.url);\n    if (!style) {\n      style = new Style(styleFileUrl);\n      styleList.push(style);\n    };\n\n    let styleText;\n    if (style.status !== 'complete') {\n      styleText = initialStyleText;\n      style.applyQueue.push(this);\n    } else {\n      styleText = style.text;\n    };\n\n    this.styleEle.textContent = styleText;\n    shadow.prepend(this.styleEle);\n  }\n};\n\n```\n\n\u003e Edit the code or `export.js` to fit your need.\n\nOpen your project and paste, or import `StylingAdvancedHTMLElement` from `export.js` if you don't want my freely written code ruins yours **(recommanded)**. Then, find all the string representing class `HTMLElement`, or say which your custom element's constructor extends from, **replace** them with `StylingAdvancedHTMLElement`. It will be like:\n\n```javascript\n// Before:\nclass StupidToggler extends HTMLElement {\n  constructor() {\n    super();\n  }\n};\n// After:\nclass StupidToggler extends StylingAdvancedHTMLElement {\n  constructor() {\n    super();\n  }\n};\n```\n\nDelete your previous `\u003clink\u003e`-or-`\u003cstyle\u003e`-creating code, along with `attachShadow` call. Set the url of the CSS file as a **static** property, `styleFileUrl`. A `fetch` will be used later to fetch the CSS.\n\n```javascript\n// Before:\nclass StupidToggler extends StylingAdvancedHTMLElement {\n  constructor() {\n    super();\n\n    const shadow = this.attachShadow({mode: open});\n\n    const styleEle = document.createElement('link');\n    styleEle.setAttribute('rel', 'stylesheet');\n    styleEle.setAttribute('href', './stupid-toggler.css');\n\n    shadow.appendChild(styleEle);\n  }\n};\n// After:\nclass StupidToggler extends StylingAdvancedHTMLElement {\n  static styleFileUrl = './styles/stupid-toggler.css';\n  constructor() {\n    super();\n  }\n};\n// Or you may have a function to get URLs.\nclass StupidToggler extends StylingAdvancedHTMLElement {\n  static styleFileUrl = getUrl('stupid-toggler.css');\n  constructor() {\n    super();\n\n    // Use this.shadowRoot in future code to get equal access as the `shadow` parameter.\n  }\n};\n```\n\nEverything is ready but your habit now.\n\n## Troubleshooting and Suggestions\n\nTrouble? Try openning an issue.\n\n**Q ?** I found your code can be improved!\n\n**A ?** Improve and pull request, we can discuss it!\n\n## License\n\nMIT Licensed\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperstrike%2Fstylingadvancedhtmlelement","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaperstrike%2Fstylingadvancedhtmlelement","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperstrike%2Fstylingadvancedhtmlelement/lists"}