{"id":21916386,"url":"https://github.com/dsheiko/pcss","last_synced_at":"2025-04-18T21:55:41.022Z","repository":{"id":27535885,"uuid":"31017212","full_name":"dsheiko/pcss","owner":"dsheiko","description":"Guidelines for writing scalable and maintainable style-sheets","archived":false,"fork":false,"pushed_at":"2018-04-27T16:04:14.000Z","size":480,"stargazers_count":18,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-29T06:41:21.800Z","etag":null,"topics":["bem","css","naming-conventions","styleguide"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dsheiko.png","metadata":{"files":{"readme":"Readme.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-19T13:50:25.000Z","updated_at":"2025-02-17T16:07:00.000Z","dependencies_parsed_at":"2022-09-21T02:12:49.040Z","dependency_job_id":null,"html_url":"https://github.com/dsheiko/pcss","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsheiko%2Fpcss","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsheiko%2Fpcss/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsheiko%2Fpcss/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsheiko%2Fpcss/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dsheiko","download_url":"https://codeload.github.com/dsheiko/pcss/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249553570,"owners_count":21290254,"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":["bem","css","naming-conventions","styleguide"],"created_at":"2024-11-28T19:17:45.826Z","updated_at":"2025-04-18T21:55:40.980Z","avatar_url":"https://github.com/dsheiko.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"PCSS\r\n=====\r\nver. 1.2.2\r\n\r\n**Pragmatic CSS** is a collection of guidelines for writing scalable and maintainable style-sheets. PCSS divides the\r\nwhole UI into **portable** and **reusable** components. Every component is described in a separate CSS (SASS/LESS/etc) module.\r\nPCSS's naming convention makes it easier to locate a module corresponding to a problem and encourages developer\r\non producing optimized object-oriented CSS.\r\n\r\nPCSS is standing on the shoulders of giants. It borrows Base, State and Theme rules from [SMACSS](https://smacss.com/),\r\nelement and subclass (modifier) naming conventions from  [BEM](https://en.bem.info), the idea of common OOP principles in CSS (inheritance, OCP, SRP)\r\nfrom [OOCSS](http://oocss.org/), context-independent cascading from\r\n[Modular CSS naming conventions](http://thesassway.com/advanced/modular-css-naming-conventions)\r\n\r\n\r\n# Contents\r\n* Key concepts\r\n  * [Component/Element/Subclass](#component)\r\n  * [State](#state)\r\n  * [Theme](#theme)\r\n* [File Structure Example](#a-fs)\r\n* [Naming Conventions](#a-nc)\r\n* [Selector Conventions](#a-sc)\r\n\r\n\r\n## Component\r\nClass | Location\r\n----|----\r\n`.panel` | `./component/panel/_index.scss`\r\n`.nav-bar` | `./component/nav-bar/_index.scss`\r\n\r\n**Component** is a reusable module of UI (e.g. `nav-bar`, `panel`, `form`).\r\nComponent consists of elements (e.g. `form__title`) and can be extended by subclasses.\r\n\r\n![](images/a-component-diagram.png)\r\n\r\n## Element\r\nClass | Location\r\n----|----\r\n`.panel__header` | `./component/panel/_index.scss`\r\n\r\nComponent is built of elements. Elements is an integral parts of a component and\r\ncannot be reused outside of component scope.\r\n\r\n\r\n## Subclass\r\nClass | Location\r\n----|----\r\n`.panel--primary` | ./component/panel/_primary.scss\r\n\r\nFollowing OOP practices, we inherit from a base component to a subclass\r\nFor example, when we are required of a dialog window, we create `./component/dialog/_index.scss` where we put the base styles for\r\nany dialogs in the application. Then we add `./component/dialog/_alert.scss` where we set the extending styles\r\nfor the concrete modal window. Now we refer to a concrete component in the HTML like that:\r\n\r\n```\r\n\u003cdiv class=\"dialog dialog--alert\"\u003e..\u003c/div\u003e\r\n\u003cdiv class=\"dialog dialog--prompt\"\u003e..\u003c/div\u003e\r\n```\r\n\r\n### Subclasses and elements\r\nElements are styled in scopes of subclasses:\r\n```sass\r\n.shopping-cart {\r\n// Base styles\r\n}\r\n.shopping-cart--default {\r\n  .shopping-cart__heading {\r\n    color: $color-white;\r\n  }\r\n}\r\n.shopping-cart--inverse {\r\n  .shopping-cart__heading {\r\n    color: $color-block;\r\n  }\r\n}\r\n```\r\nHere we have an abstract component `.shopping-cart` extended by `.shopping-cart--default` and `.shopping-cart--inverse`\r\nwhere the first has white heading and the second black one.\r\n\r\n\r\n## Component Example\r\n\r\n![](images/a-component.png)\r\n\r\n#### HTML\r\n```html\r\n\u003cdiv class=\"progressbar progressbar--big\"\u003e\r\n\t\u003coutput class=\"progressbar__status\"\u003e\r\n\t\tInstall is 70% complete\r\n\t\u003c/output\u003e\r\n\t\u003cprogress class=\"progressbar__progress\" value=\"70\" max=\"100\"\u003e\u003c/progress\u003e\r\n\t\u003cdiv class=\"progressbar__actions\"\u003e\r\n\t\t\u003cdiv class=\"icon icon--pause\"\u003epause\u003c/div\u003e\r\n\t\t\u003cdiv class=\"icon icon--play is-hidden\"\u003eresume\u003c/div\u003e\r\n\t\u003c/div\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\n#### ./component/progressbar/_index.scss\r\n```sass\r\n.progressbar {\r\n  position: relative;\r\n}\r\n.progressbar__progress {\r\n  border: 0;\r\n  position: absolute;\r\n  width: 100%;\r\n  bottom: 0;\r\n  left: 0;\r\n  appearance: none;\r\n  \u0026::-webkit-progress-bar {/*..*/ }\r\n  \u0026::-webkit-progress-value {/*..*/ }\r\n  \u0026::-moz-progress-bar {/*..*/ }\r\n}\r\n.progressbar__status {\r\n  display: flex;\r\n  position: relative;\r\n  font-size: 1rem;\r\n}\r\n.progressbar__actions {\r\n  position: absolute;\r\n  bottom: 0;\r\n  right: 0;\r\n  \u003e .icon { /*..*/ }\r\n}\r\n```\r\n\r\n#### ./component/progressbar/_big.scss\r\n```sass\r\n.progressbar--big \u003e .progressbar__status {\r\n  font-size: 1.6rem;\r\n  text-transform: uppercase;\r\n  padding: 16px;\r\n}\r\n```\r\n#### ./component/progressbar/_small.scss\r\n```sass\r\n.progressbar--small \u003e .progressbar__status {\r\n  font-size: 1.1rem;\r\n  text-transform: lowercase;\r\n  padding: 11px;\r\n}\r\n```\r\n\r\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eTop\u003c/a\u003e\u003c/p\u003e\r\n\r\n## State\r\n\r\nStates are toggable sets of rules that:\r\n- describe component/element states: `.is-expanded`, `.is-hidden`, `.has-error`.\r\n- alternate presentation: `.is-uppercase`, `.is-sticky`, `.is-pointer`.\r\n\r\nState CSS classes usually consist of a few rules. Unlike components they do not represent entities, but\r\nmodify object state.\r\n\r\n##### HTML\r\n```html\r\n\u003cdiv class=\"main has-error\"\u003e\r\n\u003caside class=\"sidebar is-hidden\"\u003e...\u003c/aside\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\n##### ./component/_main.scss\r\n```css\r\n.main {\r\n  /* default style */\r\n  \u0026.has-error {\r\n    /* state modified style */\r\n  }\r\n}\r\n```\r\n\r\n##### ./base/_global-state.scss\r\n```css\r\n/* Global state */\r\n.is-hidden {\r\n  display: none !important;\r\n}\r\n```\r\n\r\n\r\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eTop\u003c/a\u003e\u003c/p\u003e\r\n\r\n## Theme\r\nTheme classes used to alternate the style of a component\r\ndepending on the context.\r\n\r\n##### HTML\r\n```html\r\n\u003chtml class=\"theme-foo\"\u003e\r\n  \u003cdiv class=\"l-main\"\u003e\r\n  \u003caside class=\"sidebar\"\u003e...\u003c/aside\u003e\r\n  \u003c/div\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\n##### ./component/sidebar/_index.scss\r\n```css\r\n.sidebar {\r\n/* default style */\r\n}\r\n.theme-foo .sidebar {\r\n/* alterntive style */\r\n}\r\n```\r\n\r\n#### Programmatic Theming\r\n\r\nIf we need components to change styles according to a set theme (`.theme-baz` and\r\n`.theme-qux`), we can use a mixin like:\r\n\r\n```sass\r\n@mixin theme-dialog($theme) {\r\n  $bg: get-theme-style($theme, \"bg\");\r\n  .theme-#{$theme} .dialog {\r\n    background-color: #{$bg};\r\n  }\r\n}\r\n@each $theme in $themes {\r\n  @include theme-dialog($theme);\r\n}\r\n```\r\n\r\nWhere we have in `./base/_defenitions.scss`:\r\n```sass\r\n$themes: baz qux;\r\n@function get-theme-style($theme, $key) {\r\n  $baz-map: (\r\n    \"bg\": $baz-bg\r\n  );\r\n  $qux-map: (\r\n    \"bg\": $qux-bg\r\n  );\r\n  @if $theme == \"baz\" {\r\n    @return map-get( $baz-map, $key );\r\n  }\r\n  @if $theme == \"qux\" {\r\n    @return map-get( $qux-map, $key );\r\n  }\r\n  @return map-get( $baz-map, $key );\r\n}\r\n```\r\n\r\n#### Theme Example\r\n\r\n![](images/a-theme.png)\r\n\r\n```html\r\n\u003cdiv class=\"theme-foo\"\u003e\r\n  \u003carticle class=\"entry\"\u003e\r\n    \u003ch2 class=\"entry__heading\"\u003eLorem ipsum dolor\u003c/h2\u003e\r\n    \u003ctime datetime=\"2008-02-14 20:00\" class=\"entry__time\"\u003e2 hours ago\u003c/time\u003e\r\n  \u003c/article\u003e\r\n  \u003carticle class=\"entry\"\u003e\r\n    \u003ch2 class=\"entry__heading\"\u003eLorem ipsum dolor\u003c/h2\u003e\r\n    \u003ctime datetime=\"2008-02-14 20:00\" class=\"entry__time\"\u003e2 hours ago\u003c/time\u003e\r\n  \u003c/article\u003e\r\n  \u003carticle class=\"entry\"\u003e\r\n    \u003ch2 class=\"entry__heading\"\u003eLorem ipsum dolor\u003c/h2\u003e\r\n    \u003ctime datetime=\"2008-02-14 20:00\" class=\"entry__time\"\u003e2 hours ago\u003c/time\u003e\r\n  \u003c/article\u003e\r\n\u003c/div\u003e\r\n\r\n\u003cdiv class=\"theme-bar\u003e\r\n...\r\n\u003c/div\u003e\r\n```\r\n\r\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eTop\u003c/a\u003e\u003c/p\u003e\r\n\r\n\u003ca id=\"a-fs\"\u003e\u003c/a\u003e\r\nFile Structure\r\n-------\r\n\r\n```\r\nStyles\r\n├───component\r\n│   ├───btn\r\n│   │   _index.scss\r\n│   │   _primary.scss\r\n│   │\r\n│   └───form\r\n│       │   _index.scss\r\n│       │\r\n│       ├───auth\r\n│       │       _index.scss\r\n│       │       _login.scss\r\n│       │\r\n│       └───nav\r\n│               _index.scss\r\n│               _search.scss\r\n│\r\n└───base\r\n    │   _h5b-normalize.scss\r\n    │   _base.scss\r\n    │   _definitions.scss\r\n    │   _global-state.scss\r\n    │   _animations.scss\r\n    │\r\n    └───mixin\r\n            _media.scss\r\n\r\n\r\n```\r\n\r\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eTop\u003c/a\u003e\u003c/p\u003e\r\n\r\n\u003ca id=\"a-nc\"\u003e\u003c/a\u003e\r\nNaming Conventions\r\n-------\r\n\r\n* Class name represents source location. Let's say styles for `.form--nav--search` is expected in the file\r\n`component/form/nav/_search.scss` [File Structure](#a-fs)).\r\n* State classes are prefixed with `is-` or `has-` (e.g. `.is-hidden`, `.has-success`).\r\n* Theme classes are prefixed with `theme-`.\r\n\r\nClass | Entity\r\n----|----\r\n`.btn`, `.main-nav` | a component (only hyphen delimited names)\r\n`.main-nav__title` | element (subcomponent)\r\n`.btn--primary`, `.main-nav--landing-page` | subclass\r\n`.is-hidden`, `.has-success` | a state\r\n`.theme-default`, `.theme-garland` | a theme\r\n\r\n\r\n##### Further readings\r\n* [Modular CSS naming conventions](http://thesassway.com/advanced/modular-css-naming-conventions)\r\n* [Naming CSS Stuff Is Really Hard](http://seesparkbox.com/foundry/naming_css_stuff_is_really_hard)\r\n\r\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eTop\u003c/a\u003e\u003c/p\u003e\r\n\r\n\r\n\u003ca id=\"a-sc\"\u003e\u003c/a\u003e\r\nSelector Conventions\r\n-------\r\n\r\n### Keep selectors short\r\nRemember that browser reads selectors from right to left, long selectors may give it an extra workload. Besides it is\r\na unwanted contribution to production style-sheet file size. Deep nesting in CSS-preprocessor sources may cause\r\nthe described problems even without your awareness. So, keep nesting no more than 3-4 levels.\r\n\r\nAvoid excessive `@extend`-ing in SASS/LESS. It adds a long CSS selectors in a compiled code.\r\n\r\n### Use classes for styling, IDs and data-attributes to bind JavaScript\r\n\r\nIDs can be used in HTML for fragment identifiers and JavaScript hooks, but IDs should never be used in CSS.\r\nFunctional element attributes can be quite handy for styling (e.g. input[disabled]), but we rather avoid styling via custom attributes (data-attr)\r\nfor a better separation of concerns. When you use only classes for styling and keep IDs and attributes for\r\nJavaScript binding you get much more flexibility in moving styles across the document.\r\n\r\n\r\n### Loose Coupling (Tag Independence)\r\nAvoid qualified selectors (prepended with tag). Thus you will gain additional agility in moving classes around components.\r\n\r\n### Loose Coupling (Location Independence)\r\nAvoid long selectors with descendant/child combinators (.feed nav ul li h2).\r\nLong selectors besides harmful affect on selector performance mean that style rule-set is tied to particular\r\nlocation in the DOM. Independent selectors allow us to move components around our markup more freely.\r\n\r\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eTop\u003c/a\u003e\u003c/p\u003e\r\n\r\n## Further Reading\r\n\r\n* [When using IDs can be a pain in the class...](http://csswizardry.com/2011/09/when-using-ids-can-be-a-pain-in-the-class/)\r\n* [Code smells in CSS](http://csswizardry.com/2012/11/code-smells-in-css/)\r\n\r\n[![Analytics](https://ga-beacon.appspot.com/UA-1150677-13/dsheiko/pcss)](http://githalytics.com/dsheiko/pcss)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsheiko%2Fpcss","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdsheiko%2Fpcss","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsheiko%2Fpcss/lists"}