{"id":21890857,"url":"https://github.com/chantastic/css-patterns","last_synced_at":"2025-09-18T00:38:44.154Z","repository":{"id":29598196,"uuid":"33138244","full_name":"chantastic/css-patterns","owner":"chantastic","description":"A mostly reasonable approach to naming things in CSS","archived":false,"fork":false,"pushed_at":"2015-06-16T18:50:23.000Z","size":144,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-22T03:11:45.836Z","etag":null,"topics":[],"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/chantastic.png","metadata":{"files":{"readme":"readme.markdown","changelog":null,"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-03-30T17:45:28.000Z","updated_at":"2023-06-09T22:51:33.000Z","dependencies_parsed_at":"2022-08-24T10:01:33.413Z","dependency_job_id":null,"html_url":"https://github.com/chantastic/css-patterns","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chantastic/css-patterns","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Fcss-patterns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Fcss-patterns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Fcss-patterns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Fcss-patterns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chantastic","download_url":"https://codeload.github.com/chantastic/css-patterns/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chantastic%2Fcss-patterns/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275690595,"owners_count":25510497,"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-09-17T02:00:09.119Z","response_time":84,"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":[],"created_at":"2024-11-28T12:17:41.632Z","updated_at":"2025-09-18T00:38:44.104Z","avatar_url":"https://github.com/chantastic.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# CSS Patterns\n\nThis is how I'm writing CSS. It's hard to say if these ideas are \"good.\" I can\nsay that when I write this way things seem to fall into place and I paint myself\ninto fewer corners.\n\n# The 140 character pitch\n\nScalable CSS without fake-modules or silly naming. It's modeled after\nSOLID and Ruby decorators.\n\n## The decorator pattern\n\nDecorators are nice because they are scalable ad infinitum.\n\nWhen naming a decorator, you typically add specifiers to the left. Here's an\nexample:\n\n```ruby\nPerson\nAdminPerson\n\nBurger\nVeggieBurger\n```\n\nIf this fictional example were in Ruby, `VeggieBurger` might decorate `Burger`\nand be instantiated with a new `Burger` when created. `VeggieBurger` doesn't\n`extend` burger. It wraps it with additional behavior or attributes.\n\nThis is what we're trying to mimic in CSS.\n\nIf you have a `.VeggieBurger`, it should decorate the more generic `.Burger`\nclass. How do we do that without extension?\n\n```html\n\u003cdiv class=\"VeggieBurger Burger\"\u003e...\u003c/div\u003e\n\u003cdiv class=\"BeanBurger   Burger\"\u003e...\u003c/div\u003e\n```\n\n## Language Parts\n\n### Nouns\n\nLots of nouns. Let in rain.\n\n```css\n.Person { ... }\n.Select { ... }\n.Cat { ... }\n.Burger { ... }\n.Btn { ... }\n.Rainbow { ... }\n```\n\n### Attribute Noun\n\nUse an underscore for class attributes: `.Noune_attribute`\n\nLet's say a `.Cat` has a `.Breed`. `.Breed` could also be  a first-class noun of\nit's own. In a Rails app, Breed might be a normalized resource.\n\nIn this case `.Cat` **belongs_to** a `.Breed`, I'll use a single underscore.\n\n`.Cat_breed`\n\nThis suggests that `.Cat_breed` isn't a decorator on `.Breed` but an attribute\nof `.Cat`.\n\nThis frees us up to use `.CatBreed` as a `.Breed` decorator.\n\n### Adjectives\n\nAdjectives decorate a noun. Do the same with your classes; separate with a dash.\n\n```css\n.IrritatingPerson { ... }\n.LargeSelect { ... }\n.SpayedCat { ... }\n.VeggieBurger { ... }\n.DangerousBtn { ... }\n.DoubleRainbow { ... }\n```\n\n### verbs\n\nVerbs should be reserved for actions. These would both be legal if used on page\nactions:\n\n```css\n.DestroyBtn { ... }\n.ShowBtn { ... }\n```\n\n_used very similar to adjectives_\n\n### state-verbs (is/has/can)\n\nState should be represented by a class with one verb prefix.\n\nVerb-prefixed classes must never have a definition in the global scope:\n\n```css\n.is-current { /* illegal */ }\n.Person.is-current { /* legal */ }\n```\n\nI use `is-` as my prefix. I try to stick exclusively to `is`. If I myself doing\nverbal gymnastics just use us `is`, I'll try `has` (or `can` as a very last\nresort).\n\n\n## File system\n\nThis approach wins on the file system.\n\nBecause classes are well named, each class should have it's own file:\n\n```\n.\n..\nSelect.css\nPersonSelect.css\nShowPersonSelect.css\nLargeShowPersonSelect.css\n```\n\nContrast the way other conventions are typically written to files:\n\n```\n.\n..\nSelect.css\n```\n\nIn that file would be all of the \"modifiers\" associated with that file.\n\nYes, there's nothing keeping you from writing out files for modifiers. But this\nseems nonsensical:\n\n```\n.\n..\nSelect.css\nSelect--small.css\nSelect--large.css\n```\n\nThe thinking is out of place because select in bound to need more\ncontext-specific modifications. There are too many questions for answers. Does\nit justify a new file? Do I tack on context to an existing\nmodifier(`Select--small--person-show`)? Is this modification reusable? Should it\nbe(`Select--small--not-as-small-as-small-though`).\n\nThe decorator pattern answers those questions.\n\n* New File? Yes\n* Do a add context to an existing class? Yes\n* Is the new class reusable in unrelated contexts? No\n\n## Composition\n\nClasses are composed of a single class which may be composed of a single class,\nwhich may be composed of a single class, at infinitum. This is *not* inheritance\nor extension. A decorator has a dependency on all of its more generic classes.\n\n```\n+---------------------------+\n| LargeShowPersonSelect.css |\n|                           |\n| +-------------------------+\n| |    ShowPersonSelect.css |\n| |                         |\n| |   +---------------------+\n| |   |    PersonSelect.css |\n| |   |                     |\n| |   |     +---------------+\n| |   |     |    Select.css |\n+-+---+-----+---------------+\n```\n\n## Errors\n\nIt's possible in a system like this to have errors.\n\n```css\n.LargeShowPersonSelect:not(.ShowPersonSelect),\n.LargeShowPersonSelect:not(.PersonSelect),\n.LargeShowPersonSelect:not(.PersonSelect) {\n  position: relative !importante;\n}\n\n.LargeShowPersonSelect:not(.ShowPersonSelect)::before,\n.LargeShowPersonSelect:not(.PersonSelect)::before,\n.LargeShowPersonSelect:not(.PersonSelect)::before {\n  position: absolute !important;\n    width: 100% !important;\n    height: 100% !important;\n  content: \"CSS error\" !important;\n  background-color: red !important;\n  color: white !important;\n}\n\n.LargeShowPersonSelect:not(.ShowPersonSelect) {\n  content: \"dependency .show-person-select not provided\";\n}\n\n.LargeShowPersonSelect:not(.PersonSelect) {\n  content: \"dependency .person-select not provided\";\n}\n\n.LargeShowPersonSelect:not(.PersonSelect) {\n  content: \"dependency .select not provided\";\n}\n```\n\n## SOLID\n\nThis approach follows interpretations of SOLID. Here's how.\n\n### Single responsibility\n\n`.Burger` should do only one thing\u0026mdash;visually represent a `.Burger`. If it's\nexpected to do more, it should be decorated to do that.\n\n```css\n.Burger {\n  display: block;\n  color: pinkish\n}\n\n.VeggieBurger { color: green }\n\n.BeanBurger { color: lightbrown }\n```\n\n```html\n\u003cdiv class=\"Burger\"\u003e  ... \u003c/div\u003e\n\u003cdiv class=\"VeggieBurger Burger\"\u003e ... \u003c/div\u003e\n\u003cdiv class=\"BeanBurger   Burger\"\u003e ... \u003c/div\u003e\n```\n\nIt is not appropriate is to change `.burger` based on context:\n\n```css\n/* illegal */\n.Veggie .Burger { ... }\n.Bean .Burger { ... }\n```\n\n### open-closed\n\nEagerly decorate classes; resist changing them.\n\n```css\n.Person { ... }\n.AdminPerson { ... }\n.OwnerAdminPerson { ... }\n```\n\n```html\n\u003cdiv class=\"Person\"\u003e ... \u003c/div\u003e\n\u003cdiv class=\"AdminPerson Person\"\u003e ... \u003c/div\u003e\n\u003cdiv class=\"OwnerAdminPerson Person\"\u003e ... \u003c/div\u003e\n```\n\nA simple rule is this: the fewer nouns/adjectives/etc., the more resistant you should\nbe to changing that class. It must apply to *ALL* downstream classes.\n\n### Liskov Substitution Principle / Interface Segregation Principle\n\nComposition \u003e Inheritance.\n\n`@extend` is the kind of tricky. You'll be tempted to use it. Resist that\ntemptation. Your code will be better, more adaptable, and easier to reason\nabout if you resist.\n\nErr on the side of creating too many classes.\n\n### Dependency Inversion Principle\n\nClasses that decorate have an implicit dependency on the classes that they\ndecorate. `.AdminPerson` depends on `.Person`. `.OwnerAdminPerson` depends on\nboth `.AdminPerson` and `.Person`.\n\n`.Person` can be replaced substitute class that fulfills the same expectations:\n\n```css\n.Person       { display: inline-block }\n\n.AdminPerson { background-color: gold }\n\n.mock-inline  { display: inline-block }\n```\n\n```html\n\u003c!-- all the same --\u003e\n\u003cdiv class=\"AdminPerson Person\"\u003e ... \u003c/div\u003e\n\u003cdiv class=\"AdminPerson mock-inline\"\u003e ... \u003c/div\u003e\n\u003cdiv class=\"AdminPerson\" style=\"display: inline-block\"\u003e ... \u003c/div\u003e\n```\n\n# Further Reading\n\n* [ROCSS, for working with JSON APIs](https://github.com/chantastic/rocss)\n\n# Objections\n\n## What about the idea of not coupling styles to models\n\nI see this as a Scale\u0026reg; concern. Most systems I've worked are worse for\nbeing prematurely concerned with \"scale\" and normalizing classes to early.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchantastic%2Fcss-patterns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchantastic%2Fcss-patterns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchantastic%2Fcss-patterns/lists"}