{"id":17046613,"url":"https://github.com/sebobo/shel.criticalcss","last_synced_at":"2025-08-18T08:10:55.484Z","repository":{"id":35854131,"uuid":"219770230","full_name":"Sebobo/Shel.CriticalCSS","owner":"Sebobo","description":"Allow adding and combining inline (scoped) styles in Neos CMS","archived":false,"fork":false,"pushed_at":"2025-06-26T12:21:21.000Z","size":100,"stargazers_count":10,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-02T00:28:10.803Z","etag":null,"topics":["critical-css","hacktoberfest","inline-styles","neos-cms"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sebobo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":"FUNDING.yml","license":"LICENSE.txt","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,"zenodo":null},"funding":{"patreon":"shelzle","github":"sebobo"}},"created_at":"2019-11-05T14:51:23.000Z","updated_at":"2025-06-26T12:20:58.000Z","dependencies_parsed_at":"2025-04-12T15:49:35.936Z","dependency_job_id":null,"html_url":"https://github.com/Sebobo/Shel.CriticalCSS","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/Sebobo/Shel.CriticalCSS","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sebobo%2FShel.CriticalCSS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sebobo%2FShel.CriticalCSS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sebobo%2FShel.CriticalCSS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sebobo%2FShel.CriticalCSS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sebobo","download_url":"https://codeload.github.com/Sebobo/Shel.CriticalCSS/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sebobo%2FShel.CriticalCSS/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270962391,"owners_count":24675965,"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","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"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":["critical-css","hacktoberfest","inline-styles","neos-cms"],"created_at":"2024-10-14T09:46:53.548Z","updated_at":"2025-08-18T08:10:55.458Z","avatar_url":"https://github.com/Sebobo.png","language":"PHP","funding_links":["https://patreon.com/shelzle","https://github.com/sponsors/sebobo"],"categories":[],"sub_categories":[],"readme":"# Adding and combining inline styles for critical CSS in Neos CMS\n\n[![Latest Stable Version](https://poser.pugx.org/shel/critical-css/v/stable)](https://packagist.org/packages/shel/critical-css)\n[![License](https://poser.pugx.org/shel/critical-css/license)](https://packagist.org/packages/shel/critical-css)\n[![Travis Build Status](https://travis-ci.org/Sebobo/Shel.CriticalCSS.svg?branch=master)](https://travis-ci.org/Sebobo/Shel.CriticalCSS)\n[![StyleCI](https://styleci.io/repos/219770230/shield?style=flat)](https://styleci.io/repos/219770230)\n\nThis package provides several helpers to allow adding inline styles to Fusion components in Neos CMS\nand combining them locally or into the head of a html document.\nThe styles are automatically scoped by default and can be defined dynamically like any other Fusion object.\n\nCSS classes are dynamically generated based on the hash of the defined styles.\n\nThis allows you to keep styles together with a component in one file and also be able to override them.\nA good use case for this is to define inline styles for your critical CSS that should be loaded instantly when\nthe site is shown and defer loading other styles that are not needed immediately.\n\nUse cases:\n\n* Define critical CSS that should be available for \"above the fold\" content.  \n* Define dynamic CSS variables for themed websites.\n* Include CSS with reusable components.\n* Have a minimal set of CSS for each individual page.\n\nSupported:\n\n* Scoped styles   \n* Style nesting\n* Global styles with specific selectors\n* Automatic style merging into document head via http component\n* `@media` and `@supports` queries\n\nThis package doesn't require any specific browser features.\n\nThe inspiration for this package came from [SvelteJS](https://svelte.dev) and [JSS](https://cssinjs.org).\n\nTable of contents\n\n* [Installation](#installation)\n* [Usage](#usage)\n  * [Adding inline styles](#adding-inline-styles)\n    * [Nesting styles](#nesting-styles)\n    * [Using multiple styles in one component](#using-multiple-styles-in-one-component)\n    * [Using the shorthand Fusion helper](#using-the-shorthand-fusion-helper)\n    * [Modifying styles with props](#modifying-styles-with-props)\n    * [Usage with CSS variables](#usage-with-css-variables)\n    * [Custom selectors](#custom-selectors)\n    * [Insert styles from a file](#insert-styles-from-a-file)\n    * [Other examples](#other-examples)\n  * [Modifying the styles collector behaviour](#-modifying-the-styles-collector-behaviour)\n    * [Fusion based style collector](#fusion-based-style-collector)\n  * [Limitations](#limitations)\n    * [Inserting new elements in the Neos UI](#inserting-new-elements-in-the-neos-ui)\n  \n## Installation\n\nRun this in your site package:\n\n    composer require --no-update shel/critical-css\n    \nThen run `composer update` in your project root.\n                                        \n## Usage\n\n### Adding inline styles\n\nGiven the following example component:\n\n    prototype(My.Site:Component.Blockquote) \u003c prototype(Neos.Fusion:Component) {\n        text = ''\n        \n        renderer = afx`\n            \u003cblockquote\u003e{props.text}\u003c/blockquote\u003e\n        `\n        \n        @process.styles = Shel.CriticalCSS:Styles {\n            font-size = '2em'\n            font-family = 'Comic Sans'\n            padding = '.5rem'\n        }\n    }\n    \nThe resulting HTML will look similar to this:\n\n    \u003cstyle data-inline\u003e.style--cd7c679e3d{font-size:2m;font-family:Comic Sans;padding:.5rem;}\u003c/style\u003e\n    \u003cblockquote class=\"style--cd7c679e3d\"\u003eMy text\u003c/blockquote\u003e\n    \nThis will already work but you will have a lot of style tags this way and possible duplicates.\n\nThis package automatically applies a \"style collector\" to the document which collects all these inline styles.\nSo all style tags will first collected, then merged into one list, duplicates removed and inserted into the HTML head\nas one style tag.      \n\nBe careful when applying the styles not only to one tag but to a group of tags. Then it will\nautomatically generate a wrapping tag similar to how the Augmenter in Fusion works.\n\n#### Nesting styles\n\nAgain a similar example but with nested styles\n\n    prototype(My.Site:Component.Blockquote) \u003c prototype(Neos.Fusion:Component) {\n        text = ''\n        link = ''\n        \n        renderer = afx`\n            \u003cblockquote\u003e\n                {props.text} \n                \u003ca href={props.link}\u003eRead more\u003c/a\u003e\n            \u003c/blockquote\u003e\n        `\n        \n        @process.styles = Shel.CriticalCSS:Styles {\n            font-size = '2em'\n            font-family = 'Comic Sans'\n            padding = '.5rem'\n            a {\n                color = 'blue'\n                text-decoration = 'underline'\n            }\n        }\n    }           \n    \nWhen nesting you can either use `Neos.Fusion:DataStructure` or simple nesting with braces like in the example.              \n    \nThe resulting HTML will look similar to this:\n\n    \u003cstyle data-inline\u003e.style--cd7c679e3d{font-size:2m;font-family:Comic Sans;padding:.5rem;}.style--cd7c679e3d a{color:blue;text-decoration:underline;}\u003c/style\u003e\n    \u003cblockquote class=\"style--cd7c679e3d\"\u003eMy text\u003c/blockquote\u003e\n\n#### Using multiple styles in one component\n\nTo style several parts of a component you can do the following:\n\n    prototype(My.Site:Component.Complex) \u003c prototype(Neos.Fusion:Component) {\n        headline = ''\n        text = ''\n        footer = ''\n        \n        renderer = afx`\n            \u003csection\u003e\n                \u003cheader\u003e\n                    \u003cShel.CriticalCSS:Styles @path=\"@process.styles\"\n                        font-weight=\"bold\"\n                        color=\"black\"\n                    /\u003e\n                    {props.headline}\n                \u003c/header\u003e\n                \n                \u003cdiv\u003e{props.text}\u003c/div\u003e\n                \n                \u003cfooter\u003e\n                    \u003cShel.CriticalCSS:Styles @path=\"@process.styles\"\n                        font-size=\"80%\"\n                        color=\"gray\"\n                    /\u003e\n                    {props.footer}\n                \u003c/footer\u003e\n            \u003c/section\u003e\n        `\n        \n        @process.styles = Shel.CriticalCSS:Styles {\n            padding = '.5rem'\n            div {\n                margin = '1rem 0'\n            }\n        }\n    }    \n    \nThis will result in three style tags with three different css classes. All of them will be picked up \nby the collector.          \n    \n#### Using the shorthand Fusion helper\n\nSomewhere in your package define the short prototype:\n\n    prototype(CSS:Style) \u003c prototype(Shel.CriticalCSS:Styles)\n    \n(Note the needed colon:  `CSS`**`:`**`Style`. Fusion doesn't mind, but afx would convert `CssStyle` to a `Neos.Fusion:Tag`. Afx needs a way to differentiate.)\n\nThen you can write the previous example like this:  \n   \n    prototype(My.Site:Component.Complex) \u003c prototype(Neos.Fusion:Component) {\n        headline = ''\n        text = ''\n        footer = ''\n        \n        renderer = afx`\n            \u003csection\u003e\n                \u003cheader\u003e\n                    \u003cCSS:Style @path=\"@process.styles\"\n                        font-weight=\"bold\"\n                        color=\"black\"\n                    /\u003e\n                    {props.headline}\n                \u003c/header\u003e\n                \n                \u003cdiv\u003e{props.text}\u003c/div\u003e\n                \n                \u003cfooter\u003e\n                    \u003cCSS:Style @path=\"@process.styles\"\n                        font-size=\"80%\"\n                        color=\"gray\"\n                    /\u003e\n                    {props.footer}\n                \u003c/footer\u003e\n            \u003c/section\u003e\n        `\n        \n        @process.styles = CSS:Style {\n            padding = '.5rem'   \n            div {\n                margin = '1rem 0'\n            }\n        }\n    }   \n\n#### Modifying styles with props\n\nThis method allows you to modify styles easily by using props:\n\n    prototype(My.Site:Component.TextBanner) \u003c prototype(Neos.Fusion:Component) {\n        text = ''\n        backgroundColor = '#4444ff'        \n        \n        renderer = afx`\n            \u003cdiv\u003e\n                \u003cShel.CriticalCSS:Styles @path=\"@process.styles\"\n                    background-color={props.backgroundColor}\n                /\u003e\n                {props.text}\n            \u003c/div\u003e\n        `\n    }   \n    \nRemember you can only access the props when the styles object is applied somewhere inside the `renderer`.\nYou can do this either via `renderer.@process.styles` or like in the example code.\n    \n#### Usage with CSS variables\n\nYou can also easily define global or local CSS variables this way:\n\n    prototype(My.Site:Document.Page) \u003c prototype(Neos.Neos:Page) {\n        body {\n            content {\n                main = Neos.Neos:PrimaryContent {\n                    nodePath = 'main'\n                    \n                    @process.styles = Shel.CriticalCSS:Styles {\n                        --theme-background = ${q(site).property('themeBackground')}\n                        --theme-color = ${q(site).property('themeColor')}\n                    }\n                }\n            }\n        }\n    }\n    \nAnd when you then have a nested component like this:    \n \n    prototype(My.Site:Component.Blockquote) \u003c prototype(Neos.Fusion:Component) {\n        text = ''\n        \n        renderer = afx`\n            \u003cblockquote\u003e{props.text}\u003c/blockquote\u003e\n        `\n        \n        @process.styles = Shel.CriticalCSS:Styles {\n            color = 'var(--theme-color)'\n        }\n    }\n    \nI can recommend to use my [colorpicker](https://github.com/Sebobo/Shel.Neos.ColorPicker) for Neos CMS when\nallowing an editor to define a theme and then put those values into CSS variables. \n\n#### Custom selectors\n\nIt's also possible to use custom selectors to target the `html`, `body` or all tags `*`:\n \n    prototype(Neos.Neos:Page) {\n        body {                  \n            @process.globalStyles = Neos.Fusion:Join {\n                all = Shel.CriticalCSS:Styles {\n                    selector = '*'\n                    box-sizing = 'border-box'\n                }\n                body = Shel.CriticalCSS:Styles {\n                    selector = 'body'\n                    background-color = 'blue'\n                }\n            }\n        }\n    }\n\n#### Insert styles from a file\n\nThe package has a Fusion helper to insert styles inline from a file.\nDo scoping is done, but the style tag will be picked up by the style collector.\n\n    prototype(My.Site:Component.Test) \u003c prototype(Neos.Fusion:Component) {\n        text = ''\n        \n        renderer = afx`\n            \u003cShel.CriticalCSS:LoadStyles path=\"resource://My.Site/Private/Fusion/Components/Test/style.css\"/\u003e        \n            \u003cdiv\u003e{props.text}\u003c/div\u003e\n        `\n    }\n    \nAlso works as `process`:                                                         \n\n    prototype(My.Site:Component.Test) \u003c prototype(Neos.Fusion:Component) {\n        text = ''\n        \n        renderer = afx`    \n            \u003cdiv\u003e{props.text}\u003c/div\u003e\n        `\n        \n        @process.addStyles = Shel.CriticalCSS:LoadStyles {\n            path=\"resource://My.Site/Private/Fusion/Components/Test/style.css\"\n        }    \n    }\n\n#### Other examples\n\nYou can also take a look at the [functional test fixtures](Tests/Functional/Fixtures/Fusion/Styles.fusion) to see the verified use cases.\n               \n### Modifying the styles collector behaviour\n\nBy default the collector is applied as http middleware at the end of the request chain. \nIt will merge all inline styles generated with this package into one style tag in the html head.\nDuplicates are removed during this process.\nYou can disable this behaviour with this setting: \n\n    Shel:\n        CriticalCSS:\n            mergeStyles:\n                enabled: false\n                \n#### Fusion based style collector                \n                \nThis package contains a second collection method via Fusion. \nThe `Shel.CriticalCSS:StyleCollector` helper can be used in a similar way as `process` to merge \nall inline style tags into one. When doing this inside the DOM the style tag will be prepended wherever\nit is applied to. When applying it to the whole document, it will also merge the styles into the html head.\n\nThis method can be helpful in certain cases but has issues when the contained components have\ntheir own cache configurations. This is why the http component is preferred to solve this on the\ndocument level. But if for some reason you cannot use the default, this might help.\n \n### Limitations\n\n#### Inserting new elements in the Neos UI\n\nWhen working the backend the styles collector will not automatically pick up style blocks from newly inserted elements.\nThis might cause some issues when the added elements are somehow treated with Javascript like in Sliders.\n\n#### Caching\n\nAs explained in the styles collector section, the Fusion based collector has issues with cached components that\nhave styles applied to them. This can result in styles missing from the document.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebobo%2Fshel.criticalcss","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebobo%2Fshel.criticalcss","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebobo%2Fshel.criticalcss/lists"}