59 |
60 |
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CSS Style guide
2 |
3 | This is the style guide that I use to write CSS. It's inspired by other
4 | methodologies like BEM, SMACSS, OOCSS, etc but more simple. The aim of this
5 | guide is **write modular, scalable and maintainable CSS.**
6 |
7 | ## Components
8 |
9 | CSS components (a.k.a. modules, objects, etc) are defined with classes.
10 |
11 | ```css
12 | /* The 'article' component */
13 | .article {}
14 | ```
15 |
16 | Don't use `-` character for component names. If you want to differentiate words,
17 | use _camelCase_ or underscores:
18 |
19 | ```css
20 | /* The 'image carousel' component */
21 | .imageCarousel {}
22 |
23 | /* Alternative naming if you don't like camelCase */
24 | .image_carousel {}
25 | ```
26 |
27 | Components may have properties. The properties are joined to the component name
28 | with a `-`. Example:
29 |
30 | ```css
31 | .article {
32 | /* styles of the component */
33 | }
34 |
35 | .article-header {
36 | /* styles of the component's property */
37 | }
38 | ```
39 |
40 | Like component names, properties shouldn't have `-` characters:
41 |
42 | ```css
43 | .article-mainImage {
44 | /* styles of the mainImage property of article component */
45 | }
46 | ```
47 |
48 | > [!note]
49 | >
50 | > The use of camelCase notation allows to use a simple join character unlike BEM
51 | > that needs two underscores (`.article__header__title`).
52 |
53 | This allows to add sub-properties if it's needed:
54 |
55 | ```css
56 | .article-header-title {}
57 | ```
58 |
59 | ```html
60 |
61 |
62 |
63 | The title
64 |
65 |
66 |
67 | ```
68 |
69 | Don't use use sub-properties if you really don't need to. The previous example
70 | is clearer as following:
71 |
72 | ```html
73 |
74 |
75 |
76 | The title
77 |
78 |
79 |
80 | ```
81 |
82 | ## Modifiers
83 |
84 | Components and properties may have modifiers. Modifiers are used to change
85 | styles under some contexts or status. Unlike, for instance, BEM, modifiers are
86 | independent classes that you can combine with components and properties. These
87 | classes should start with `.is-*` and `.has-*`.
88 |
89 | The `.is-*` modifier changes the element with a specific status. Some examples:
90 |
91 | ```css
92 | .article.is-hidden {
93 | display: none;
94 | }
95 |
96 | .article-header.is-important {
97 | font-weight: bold;
98 | }
99 |
100 | .article p.is-highlighted {
101 | background: yellow;
102 | }
103 | ```
104 |
105 | ```html
106 |
107 | invisible content
108 |
109 | ```
110 |
111 | The `.has-*` modifier is used to change the element according to its content.
112 | Example:
113 |
114 | ```css
115 | .article {
116 | width: 500px;
117 | }
118 |
119 | .article.has-video {
120 | width: 900px;
121 | }
122 | ```
123 |
124 | ```html
125 |
126 |
127 |
128 | ```
129 |
130 | The modifiers must be declared always combined with the components/properties:
131 |
132 | ```css
133 | /* wrong */
134 | .has-video {
135 | width: 900px;
136 | }
137 |
138 | /* right */
139 | .article.has-video {
140 | width: 900px;
141 | }
142 | ```
143 |
144 | This has two advantages:
145 |
146 | - Increments the priority of the selector.
147 | - Allows to create different styles for the same modifier combined with
148 | different components/properties.
149 |
150 | There may be global modifiers if needed:
151 |
152 | ```css
153 | /* This is a global modifier, it can be combined with any component */
154 | .is-hidden {
155 | display: none;
156 | }
157 | ```
158 |
159 | Some `has-` modifiers can be replaced with the
160 | [`:has()` selector](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)
161 | supported by all modern browsers:
162 |
163 | ```css
164 | /* Old way */
165 | .article.has-video {
166 | width: 900px;
167 | }
168 |
169 | /* Modern way */
170 | .article:has(.article-video) {
171 | width: 900px;
172 | }
173 | ```
174 |
175 | ## Layouts
176 |
177 | By convention, CSS styles used only for layout purposes must use the `.ly-*`
178 | namespace. Example of the `2columns` layout with two properties: `navigation`
179 | and `content`:
180 |
181 | ```css
182 | .ly-2columns {
183 | display: grid;
184 | grid-template-areas: "navigation content";
185 | grid-template-columns: 400px 1fr;
186 | }
187 | .ly-2columns-navigation {
188 | grid-area: navigation;
189 | }
190 | .ly-2columns-content {
191 | grid-area: content;
192 | }
193 | ```
194 |
195 | ```html
196 |
197 |
198 | ...
199 |
200 | ```
201 |
202 | ## CSS + JS
203 |
204 | The CSS classes can be used by JavaScript to select elements. To prevent
205 | conflict between CSS and JS, **the classes used by JavaScript should not be used
206 | by CSS, and viceversa**. Because that, there's the `.js-*` namespace:
207 |
208 | ```html
209 |
210 |
Hello world
211 |
212 |
213 |
214 | Other popup
215 |
216 | ```
217 |
218 | ```js
219 | document.querySelector(".js-popup")?.style.display = "block";
220 | ```
221 |
222 | It's recomended to separate styling classes and functionality classes. This
223 | doesn't mean JavaScript can not use styling classes, but only for styling
224 | purposes:
225 |
226 | ```js
227 | document.querySelector(".article")?.classList.add("is-hidden");
228 | ```
229 |
230 | ## !important
231 |
232 | The `!important` flag **is not recommended,** but may be needed in few edge
233 | cases. Every time we use it, we should include a comment explaining the reason,
234 | so it can be removed in the future.
235 |
236 | ```css
237 | .popup-title {
238 | font-weight: bold
239 | !important; /* This overrides the inline style applied by the plugin popup */
240 | }
241 | ```
242 |
243 | ## Imports
244 |
245 | In order to avoid problems with the selectors priority, the main CSS file should
246 | import the nested CSS files in the following order:
247 |
248 | - global styles (reset, variables, normalize, etc)
249 | - components
250 | - global modifiers
251 | - hacks
252 |
253 | Example:
254 |
255 | ```css
256 | /* Global styles */
257 | @import "normalize.css";
258 | @import "reset.css";
259 |
260 | /* Components */
261 | @import "components/article.css";
262 | @import "components/comments.css";
263 |
264 | /* Global modifiers and hacks */
265 | @import "modifiers.css";
266 | @import "ie.css";
267 | ```
268 |
269 | ## Layers
270 |
271 | Additionally, we can use CSS layers for better organization and priority:
272 |
273 | ```css
274 | @layer global {
275 | /* Normalize, reset */
276 | }
277 |
278 | @layer components {
279 | /* Components */
280 | }
281 |
282 | @layer utils {
283 | /* Utils, global modifiers, hacks, etc */
284 | }
285 | ```
286 |
287 | ---
288 |
289 | Other notes:
290 |
291 | - [100vh](cases/100vh)
292 | - [quantity](cases/quantity)
293 | - [typography](cases/typography)
294 | - [select (filamentgroup)](https://github.com/filamentgroup/select-css)
295 |
--------------------------------------------------------------------------------