summaryrefslogtreecommitdiff
path: root/memos/WM-086.txt
blob: 97c5f1b4053466c51e554f69a08df85d34a6d567 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
Document: WM-086                                                 P. Webb
Category: Programming                                         2026-03-24

                            How to write CSS

Abstract

   CSS is about structure and design, the code should reflect that.

Body

   I've been writing HTML and CSS for 20+ years at the time of this
   writing. Along the way I've learned, discovered, and refined the way
   I write CSS. In 2026, I'm so glad I don't need Sass anymore, as much
   as I enjoyed writing it; nested code and variables are now readily
   available in browsers without a precompilation step. Success!

   However, I still run into codebases where the CSS irritates tf outta
   me. I'm not just talking random projects on Github, I mean in Fortune
   500 companies too. LLMs regurgitate bad practices so for all the new
   coders discovering the joy and beauty of web development, here's some
   tips from someone who survived the BEM syntax era and completely
   side-stepped the "let's just chain variables" frenzy that's still
   pervasive today.

   What follows are examples of what I consider to be poorly written CSS
   and how I fix them:

   ```css
   .board_icon {
   	 text-align: center;
   	 padding: 8px 4px 0px 4px;
   	width: 60px;
   	flex-shrink: 0;
   }
   .board_icon a {
   	display: inline;
   }
   .board_icon a:hover, .board_icon a:focus{
   	text-decoration: none;
   }
   .board_icon a::before {
   	 display: inline;
   	 font-family: "Font Awesome 6 Free";
   	 font-size: 2em;
   	 content: "\f086";
   }
   .board_icon a.board_on::before {
   	 font-weight: 900;
   }
   .board_icon a.board_on2::before {
   	font-weight: 900;
   }
   .board_icon a.board_off::before {
   	font-weight: 400;
   }
   .board_icon a.board_redirect::before {
   	font-weight: 900;
   	content: "\f061";
   }
   ```

   This physically pains me (if I look at it too long). Inconsistent
   indentation and lack of space between rules are the most egregrious
   errors in this for me, but you also have rules that can be
   consolidated and a unit specifier on a zero value (`0px` is
   unnecessary, just use `0`). Finally, the rules aren't in
   alphabetical order.

   Computers and browsers don't care but for humans, code you can scan
   quickly is important for collaboration; even if that collaborator is
   future you! Don't you wanna make future you's life at least a lil'
   bit easier?

   Here's how I would rewrite that code block:

   ```css
   .board_icon {
     flex-shrink: 0;
     padding: 8px 4px 0 4px;
     text-align: center;
     width: 60px;

     a {
       display: inline;

       &::before {
         content: "\f086";
         display: inline;
         font-family: "Font Awesome 6 Free";
         font-size: 2rem;
       }

       &:focus,
       &:hover {
         text-decoration: none;
       }

       &.board_off::before {
         font-weight: 400;
       }

       &.board_on,
       &.board_on2,
       &.board_redirect {
         &::before {
           font-weight: 900;
         }
       }

       &.board_redirect::before {
         content: "\f061";
       }
     }
   }
   ```

   You might've noticed I replaced the `2em` `font-size` with `2rem`.
   This is more of an aesthetic choice. Elastic Measure (`em`) and Root
   Elastic Measure (`rem`) are similar in that they scale based on
   something but `em` scales based on the parent element whereas `rem`
   scales based on the root (`html`) font size.

   When I'm building sites, I want everything to be cohesive and scale
   uniformly. That way, when I decide to change the root font size, my
   entire site won't look wonky.

   Here's a list of other things I've seen in the particular codebase
   I'm rewriting for my forum theme:

   - `margin:0 0 10px 0;`: no space after the colon
   - `border-color :rgb(199, 195, 191);`: space before the colon but not
     after? Why?
   - `border-top: 1px solid RGB(255, 255, 255);`: why is `RGB` all caps
     here but not in the previous line? Why use `rgb` at all when this
     could be represented as `#fff` or simply `white`?
   - `font-weight: bold;` and `font-weight: 700;`: these both mean bold
     and there's only system fonts declared so why specify `700` and not
     `600` (the default)?
   - `margin-top: 5px !important;`: if you have to use `!important;`,
     something's wrong. It's possible this theme is trying to override
     some styling of the core forum software so I'm willing to let this
     slide but then again, proper nesting would eliminate the need
     for this.
   - `background: #fdfdfd;`: unless you also have a background image and
     positioning, you should always use `background-color`.
     Specificity wins.
   - `font: 9px/15px verdana, sans-serif;`: I don't like this for a few
     reasons. First, `font-size` and `line-height` are just easier to
     read and should be declared in parent elements. Per element rules
     like this leads to eventual divergence and tech debt. Second, the
     font name is lowercase here and regular case elsewhere. Like so:
     `font-family: Verdana, Helvetica, Arial, sans-serif;` and this is
     applied to an `h1`, which makes sense to have a different font as
     it's a headline. I rarely use `font` and the rare times I do, it's
     to do `font: inherit` (browsers by default have buttons and inputs
     use different fonts).
   - `padding: 10px 10px;`: redundant; this is telling us that there's
     10 pixels of padding to the top and bottom, as well as left and
     right. This could be rewritten as `padding: 10px;` (10 pixels of
     padding all around).

   Now, there are certain conditions where I don't necessarily use
   alphabetical order and that's when there are rule pairs present.

   - `width` / `height`
   - `margin` / `padding`
   - `top` / `left` / `bottom` / `right`

   Here's a simple example:

   ```css
   .profile_hd {
     width: 2rem; height: 2rem;

     &::before {
       width: 100%; height: 100%;

       background-image: url("../images/emoji/bust_in_silhouette_3d.png");
       background-size: contain;
     }
   }
   ```

   And a more involved one (using CSS variables from my palette[1]). I
   left the `color: rgb` rule in there because I haven't decided what to
   replace it with (I don't like mixing color rules, stay consistent...
   similarly, I'm not sure that `margin-top` needs to be there...oh and
   `&::before` and `&::after` are grouped together in that order because
   it makes sense):

   ```css
   ul {
     background-color: var(--uchu-yang);
     border: 1px solid var(--uchu-gray-3);
     border-radius: 4px;
     box-shadow: 3px 3px 4px oklch(var(--uchu-yin-raw) / 30%);
     color: rgb(83, 100, 130);
     line-height: 2.2rem;
     margin-top: 2px;
     min-width: 18.2rem;
     padding: 0.5rem;
     position: absolute;
     z-index: 90;

     &::before,
     &::after {
       width: 0; height: 0;

       border-left: 0.5rem solid transparent;
       border-right: 0.5rem solid transparent;
       content: "";
       position: absolute;
     }

     &::before {
       top: -0.5rem; left: 1.25rem;
       border-bottom: 0.5rem solid var(--uchu-yang);
     }

     &::after {
       top: calc(calc(0.5rem + 1px) * -1); left: 1.25rem;

       border-bottom: 0.5rem solid var(--uchu-gray-3);
       z-index: -1;
     }
   }
   ```

   You can see `width` and `height` together at the top of a rule block,
   separated by a blank line because there are multiple rules after
   that. However, in the standalone `&::before` block, there's no blank
   line after `top` and `left` because that'd look silly. The `&::after`
   block has more than one rule after `top` and `left` so those lines
   are grouped together.

   I've dabbled in trying to get Prettier to format my CSS files back
   when I was in the Node.js ecosystem, with middling results. I'm sure
   I could get Claude to make a formatter to my specifications...hmm,
   side project for now.

   There are situations where I may have something like `padding: 1rem;`
   and also have `margin-right: 2rem`. I wouldn't put these together
   because of the `-right`. Non-dashed specifiers are in alphabetical
   order like everything else (including `padding` in this instance).

   This codebase has a lot of styling on IDs, which is something I don't
   do. For me, IDs are for HTML and JavaScript, not styling; either use
   the element name and style against that or apply a class to
   said element.

   For naming elements, I prefer using a dash or two and relying on
   nesting (no more than three levels) when necessary. For this project,
   I'm beholden to the existing HTML syntax in PHP files. They'll get
   updated over time.

   I'm probably missing a lot but this is just off the top of my head.
   Multi-trillion-dollar corporations perpetuate these terrible code
   choices too but at least in my personal projects I can have a curated
   and maintainable experience. 🕸️

References

   [1] <https://uchu.style>