@anyblades/blades 2.4.8 → 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. package/.prettierrc.json +6 -0
  4. package/README.md +130 -104
  5. package/context7.json +4 -0
  6. package/css/blades.css +454 -198
  7. package/css/blades.min.css +145 -0
  8. package/css/blades.standalone.css +216 -272
  9. package/css/blades.standalone.min.css +37 -0
  10. package/css/{blades.ninja.css → ninja.css} +7 -2
  11. package/css/ninja.min.css +5 -0
  12. package/css/pico.conditional.css +3189 -0
  13. package/css/pico.conditional.min.css +107 -0
  14. package/css/pico.css +3186 -0
  15. package/css/pico.min.css +105 -0
  16. package/css/postcss.config.js +23 -0
  17. package/index.html +63 -0
  18. package/package.json +27 -11
  19. package/src/blades.css +4 -3
  20. package/src/blades.standalone.css +2 -2
  21. package/src/{blades.ninja.css → ninja.css} +7 -2
  22. package/src/pico/_components.css +974 -0
  23. package/src/pico/_content.css +608 -0
  24. package/src/pico/_forms.css +747 -0
  25. package/src/pico/_index.css +12 -0
  26. package/src/pico/_layout.css +128 -0
  27. package/src/pico/_utilities.css +53 -0
  28. package/src/pico/_variables.css +671 -0
  29. package/src/pico.css +1 -0
  30. package/src/standalone/_core.css +32 -0
  31. package/src/{blades.standalone.theme.css → standalone/_theme.css} +3 -7
  32. package/src/{_utilities.css → standalone/_utilities.css} +3 -4
  33. package/src/standalone/_variables.css +20 -0
  34. package/src/{link-icon.css → standalone/components/_icon-helper.css} +0 -2
  35. package/src/{components → standalone/components}/_timeline.css +1 -2
  36. package/src/{content → standalone/content}/_code.css +9 -12
  37. package/src/{content/_typography.css → standalone/content/_list.css} +4 -49
  38. package/src/{content → standalone/content}/_table.css +4 -7
  39. package/src/standalone/content/_typography.css +38 -0
  40. package/src/{float-label.core.css → standalone/forms/_float-label.core.css} +22 -14
  41. package/src/standalone/forms/_float-label.css +2 -0
  42. package/src/{breakout.css → standalone/layout/_breakout.css} +3 -6
  43. package/src/{_layout.css → standalone/layout/_columns.css} +2 -20
  44. package/src/standalone/layout/_scroller.css +10 -0
  45. package/_config.yml +0 -5
  46. package/blades.gemspec +0 -16
  47. package/css/blades.standalone.core.css +0 -738
  48. package/css/blades.standalone.theme.css +0 -147
  49. package/css/breakout.css +0 -125
  50. package/css/float-label.core.css +0 -56
  51. package/css/float-label.css +0 -96
  52. package/css/float-label.theme.css +0 -39
  53. package/css/link-icon.css +0 -59
  54. package/css/responsive-table.css +0 -57
  55. package/src/blades.standalone.core.css +0 -21
  56. package/src/float-label.css +0 -3
  57. /package/src/{responsive-table.css → standalone/content/_table-responsive.css} +0 -0
  58. /package/src/{float-label.theme.css → standalone/forms/_float-label.theme.css} +0 -0
@@ -1,81 +1,51 @@
1
- /* Follows https://github.com/picocss/pico/blob/main/scss/_index.scss */
2
-
3
- /* Layout */
4
-
5
- /* Extends https://github.com/picocss/pico/blob/main/scss/layout/
6
- \3c !--section:docs,columns-->
7
-
8
- ### Auto-columns
9
-
10
- `.columns` automatically creates columns with at least 30 characters each:
11
-
12
- <article class="columns">
13
- <p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p>
14
- </article>
1
+ /*!
2
+ * Blades CSS standalone kit v3 🥷
3
+ * Copyright 2026 (https://blades.ninja/css/standalone/)
4
+ * Licensed under MIT
5
+ */
6
+ /*\3c !--section:code-->```css */
15
7
 
16
- The smaller the font size, the more columns will be created:
17
-
18
- <article class="columns" style="font-size: 65%">
19
- <p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p>
20
- </article>
21
-
22
- Useful for tables of contents and long lists.
23
-
24
- How it works:
25
- ```css */
8
+ /* Follows https://github.com/picocss/pico/blob/main/scss/_index.scss */
26
9
 
27
- .columns {
28
- -moz-columns: 25ch auto;
29
- columns: 25ch auto; /* common container 65ch / 25ch => 2 columns max, with a buffer for list paddings */
10
+ :scope,
11
+ :host {
12
+ /**
13
+ * Design Tokens !!! NAMING NOT FINALIZED YET !!!
14
+ * `--in-pico: *` props below are for reference only (not to be used)
15
+ */
16
+ --blades-token-primary: var(--pico-primary, dodgerblue);
17
+ --in-pico: #01aaff;
18
+ --blades-token-secondary: var(--pico-secondary, lightslategray);
19
+ --in-pico: #969eaf;
20
+
21
+ /**
22
+ * Inherited Variables
23
+ */
24
+ --blades-timeline-border-color: color-mix(
25
+ in srgb,
26
+ var(--blades-token-secondary) 50%,
27
+ transparent
28
+ );
30
29
  }
31
30
 
32
- /* Avoid breaking inside elements, such as nested lists */
33
-
34
- .columns > * {
35
- -moz-column-break-inside: avoid;
36
- break-inside: avoid;
37
- }
38
-
39
- /*```
40
- \3c !--section:docs,jump-->
41
-
42
- ### Jump to top
43
-
44
- `data-jump-to="top"` fixes element to the corner and adds extra top padding to make it easy to click:
45
- ```css */
46
-
47
- [data-jump-to="top"] {
48
- position: fixed;
49
- right: 0;
50
- bottom: 0;
51
- padding-top: 50vh;
52
- }
53
-
54
- /*```
55
- \3c !--section--> */
56
-
31
+ /* Layout */
57
32
  /*
58
33
  \3c !--section:code-->
59
34
  ```css */
60
-
61
35
  .breakout,
62
36
  .breakout-all {
63
37
  max-width: calc(10% + 65ch + 10%);
64
38
  /* Prepare the container for breakout elements */
65
39
  padding-inline: 10%;
66
40
  }
67
-
68
41
  @media (width < 768px) {
69
-
70
42
  .breakout,
71
43
  .breakout-all {
72
44
  max-width: calc(1rem + 65ch + 1rem);
73
45
  padding-inline: 1rem
74
46
  }
75
47
  }
76
-
77
48
  /* Breakout direct children only */
78
-
79
49
  .breakout > *:is(
80
50
  table,
81
51
  pre,
@@ -101,17 +71,14 @@ How it works:
101
71
  margin-left: 50%;
102
72
  transform: translateX(-50%);
103
73
  }
104
-
105
74
  /* Respect img/picture min-width */
106
-
107
75
  .breakout > *:is(img, picture):not(.does-not-exist), .breakout-all > *:is(img, picture):not(.does-not-exist) /* soft win specificity over .breakout-item above */ {
108
76
  min-width: auto;
109
77
  }
110
-
111
78
  /* Max out the width of the element */
112
-
113
79
  .breakout > *.breakout-item-max, .breakout-all > *.breakout-item-max {
114
- width: min(125%, 100dvw) !important; /* !important is for cases like figure.breakout-item-max @TODO */
80
+ width: min(125%, 100dvw) !important;
81
+ /* !important is for cases like figure.breakout-item-max @TODO */
115
82
  }
116
83
 
117
84
  .breakout-all > *:is(h2, h3, h4, h5, h6, hr):not([class]) {
@@ -154,15 +121,10 @@ How it works:
154
121
  height: 100%;
155
122
  transform: translateX(-50%);
156
123
  }
157
-
158
124
  /*```
159
- \3c !--section:summary-->
160
-
161
- The `.breakout` layout allows images, tables, and other figures to automatically extend or bleed beyond their parent container’s width.
162
-
163
125
  \3c !--section:docs-->
164
126
 
165
- ### Demo \3c !-- inside parent .breakout -->
127
+ The `.breakout` layout allows images, tables, and other figures to automatically extend or bleed beyond their parent container’s width.
166
128
 
167
129
  Break out a wide image from the text flow:
168
130
 
@@ -196,23 +158,57 @@ Or anything else:
196
158
  <hr>
197
159
 
198
160
  \3c !--section--> */
161
+ /*\3c !--section:docs-->
199
162
 
200
- /* Content */
163
+ `.columns` automatically creates columns with at least 30 characters each:
164
+
165
+ <article class="columns">
166
+ <p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p>
167
+ </article>
168
+
169
+ The smaller the font size, the more columns will be created:
170
+
171
+ <article class="columns" style="font-size: 65%">
172
+ <p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p>
173
+ </article>
174
+
175
+ Useful for tables of contents and long lists.
176
+
177
+ \3c !--section:code-->
178
+ ```css */
179
+ .columns {
180
+ -moz-columns: 25ch auto;
181
+ columns: 25ch auto; /* common container 65ch / 25ch => 2 columns max, with a buffer for list paddings */
182
+ }
183
+ /* Avoid breaking inside elements, such as nested lists */
184
+ .columns > * {
185
+ -moz-column-break-inside: avoid;
186
+ break-inside: avoid;
187
+ }
188
+ /*```\3c !--section:docs-->
201
189
 
202
- /* Extends https://github.com/picocss/pico/blob/main/scss/content/_typography.scss
203
- \3c !--section:docs,h-anchor-->
190
+ `<a href="#top">` is automatically fixed to the bottom-right corner with extra top padding for easier clicking:
191
+ ```css */
192
+ [href="#top"] {
193
+ position: fixed;
194
+ right: 0;
195
+ bottom: 0;
196
+ padding-top: 50vh;
197
+ }
204
198
 
205
- ### Heading anchors
199
+ /* Content */
200
+ /*\3c !--section:docs-->
201
+
202
+ ## Heading anchors
206
203
 
207
204
  Links with `href="#..."` inside headings are automatically displayed as anchors:
208
205
 
209
206
  <article>
210
- <h2 style="margin: 0 0 0 1.5rem">Heading with anchor <a href="#hwa" style="visibility: visible">#</a></h2>
207
+ <h2 style="margin: 0 0 0 1.5rem">Heading with anchor <a aria-hidden="true" style="visibility: visible">#</a></h2>
211
208
  </article>
212
209
 
213
210
  How it works:
214
211
  ```css */
215
-
216
212
  h1,
217
213
  h2,
218
214
  h3,
@@ -221,7 +217,6 @@ h5,
221
217
  h6 {
222
218
  position: relative;
223
219
  }
224
-
225
220
  h1 a[aria-hidden="true"], h2 a[aria-hidden="true"], h3 a[aria-hidden="true"], h4 a[aria-hidden="true"], h5 a[aria-hidden="true"], h6 a[aria-hidden="true"] {
226
221
  visibility: hidden;
227
222
  position: absolute;
@@ -232,20 +227,14 @@ h1 a[aria-hidden="true"], h2 a[aria-hidden="true"], h3 a[aria-hidden="true"], h4
232
227
  color: silver;
233
228
  text-decoration: none;
234
229
  }
235
-
236
230
  @media /* to avoid double-tap on touch devices */ (hover: hover) {
237
231
  h1:hover a[aria-hidden="true"], h2:hover a[aria-hidden="true"], h3:hover a[aria-hidden="true"], h4:hover a[aria-hidden="true"], h5:hover a[aria-hidden="true"], h6:hover a[aria-hidden="true"] {
238
232
  visibility: visible;
239
233
  }
240
234
  }
235
+ /*\3c !--section:docs-->
241
236
 
242
- /*```
243
-
244
- **PRO** example of automatic anchors for `11ty`+`markdown-it-anchor`: https://github.com/anyblades/eleventy-blades/blob/main/src/eleventy.config.js
245
-
246
- \3c !--section:docs,list-->
247
-
248
- ### List markers
237
+ ## Custom markers
249
238
 
250
239
  Customize markers using inline `style="--list-marker:..."` on `<ul>/<ol>` or even individual `<li>`:
251
240
 
@@ -264,26 +253,21 @@ Customize markers using inline `style="--list-marker:..."` on `<ul>/<ol>` or eve
264
253
 
265
254
  How it works:
266
255
  ```css */
267
-
268
256
  ul[style*="--list-marker:"], ol[style*="--list-marker:"] {
269
257
  list-style-type: var(--list-marker);
270
258
  }
271
-
272
259
  ul[style*="--list-marker:"] > li, ol[style*="--list-marker:"] > li {
273
260
  list-style-type: inherit;
274
261
  }
275
-
276
262
  ul li[style*="--list-marker:"], ol li[style*="--list-marker:"] {
277
263
  list-style-type: var(--list-marker);
278
264
  }
279
-
280
265
  ul li[data-marker]::marker, ol li[data-marker]::marker {
281
266
  content: attr(data-marker) " "; /* ⚠️ Chrome and Firefox only */
282
267
  }
283
-
284
268
  /*```
285
269
 
286
- ### Markerless
270
+ ## Markerless
287
271
 
288
272
  `.markerless` removes markers and reduces padding:
289
273
 
@@ -298,18 +282,15 @@ ul li[data-marker]::marker, ol li[data-marker]::marker {
298
282
 
299
283
  How it works:
300
284
  ```css */
301
-
302
285
  ul.markerless, ol.markerless {
303
286
  padding-inline-start: 1.25rem;
304
287
  }
305
-
306
288
  ul.markerless li, ol.markerless li {
307
289
  list-style: none;
308
290
  }
309
-
310
291
  /*```
311
292
 
312
- ### Unlist
293
+ ## Unlist
313
294
 
314
295
  `.unlist` removes list styling at all:
315
296
 
@@ -326,7 +307,6 @@ ul.markerless li, ol.markerless li {
326
307
 
327
308
  How it works:
328
309
  ```css */
329
-
330
310
  ul.unlist,
331
311
  ul.unlist-all,
332
312
  .unlist-all ul,
@@ -335,87 +315,12 @@ ul.unlist,
335
315
  .unlist-all ol {
336
316
  padding-inline-start: 0;
337
317
  }
338
-
339
318
  ul.unlist > li, ul.unlist-all > li, .unlist-all ul > li, ol.unlist > li, ol.unlist-all > li, .unlist-all ol > li {
340
319
  list-style: none;
341
320
  }
321
+ /*\3c !--section:docs-->
342
322
 
343
- /*```
344
- \3c !--section--> */
345
-
346
- /* Extends https://github.com/picocss/pico/blob/main/scss/content/_link.scss
347
- \3c !--section:code-->
348
- ```css */
349
-
350
- /* Use inline flex only if link contains an icon */
351
-
352
- a:not(.does-not-exist):has(> i) {
353
- display: inline-flex;
354
- overflow-y: clip; /* to work in pair with text-underline-offset in Safari */
355
- gap: 0.375ch; /* =3/8 */
356
- text-wrap: balance;
357
- }
358
-
359
- a:not(.does-not-exist) > i {
360
- float: left; /* ✅ Chrome ❌ Safari */
361
- font-style: normal;
362
- text-underline-offset: -2em; /* ❌ Chrome ✅ Safari - to clip it with overflow-y */
363
- }
364
-
365
- /* Favicons */
366
-
367
- a:not(.does-not-exist) > i > img {
368
- display: inline-block; /* for Tailwind CSS Typography */
369
- max-width: none; /* to keep ratio safe */
370
- height: 1.25em;
371
- margin-block-start: calc(-0.25em / 2);
372
- }
373
-
374
- a:not(.does-not-exist) > i[class^="fa-"],
375
- a:not(.does-not-exist) > i[class*=" fa-"] {
376
- line-height: inherit;
377
- --fa-width: auto;
378
- }
379
-
380
- /* Font Awesome */
381
-
382
- a:not(.does-not-exist) > i.fa-lg {
383
- line-height: normal;
384
- }
385
-
386
- /*```
387
- \3c !--section:summary-->
388
-
389
- Use *Bl*ades `<i>`-helper to wrap emojis, favicons, or simply drop Font Awesome icons inside links.
390
- It automatically handles sizing and alignment while preventing underline on icons.
391
-
392
- \3c !--section:docs-->
393
-
394
- <article class="breakout-item">
395
-
396
- | Use `<i>`-helper with | Clean HTML without `<span>ning` |
397
- | ------------------------------------------------------------------------------------- | ------------------------------------ |
398
- | [<i>🥷</i> Emojis](#) | `<a><i>🥷</i> Text</a>` |
399
- | [<i>![](https://www.google.com/s2/favicons?domain=any.digital&sz=64)</i> Favicons](#) | `<a><i><img src="..."></i> Text</a>` |
400
- | [<i class="fa-brands fa-github fa-lg"></i> Font Awesome icons](#) | `<a><i class="fa-..."></i> Text</a>` |
401
- </article>
402
-
403
- Or even for:
404
-
405
- <article>
406
- <a href="#" style="margin: 0 0 0 1rem"><i>🥷</i> very-very-very-very-very-<br>long-multiline-links</a>
407
- </article>
408
-
409
- How we made it: https://codepen.io/editor/anydigital/pen/019d2b94-5616-75dc-a23e-e111869d5237
410
-
411
- **PRO** example of automatic favicons for `11ty`: https://blades.ninja/build-awesome-11ty/processors/#auto-link-favicons
412
-
413
- \3c !--section--> */
414
-
415
- /* Extends https://github.com/picocss/pico/blob/main/scss/content/_table.scss
416
- \3c !--section:docs-->
417
-
418
- ### Column expanders
323
+ ## Column expanders
419
324
 
420
325
  Place `<hr>` element inside `<th>` column to expand it horizontally:
421
326
 
@@ -434,25 +339,21 @@ Living examples of big tables with `<hr>`-expanders: \3c !--A-Z-->
434
339
 
435
340
  How it works:
436
341
  ```css */
437
-
438
342
  th hr {
439
343
  visibility: hidden;
440
344
  width: 12ch; /* min ~65/12 = ~5 cols */
441
345
  height: 0;
442
346
  margin: 0;
443
347
  }
444
-
445
348
  th hr.lg {
446
349
  width: 18ch;
447
350
  }
448
-
449
351
  th hr.x2 {
450
352
  width: 24ch;
451
353
  }
452
-
453
354
  /*```
454
355
 
455
- ### Borderless table
356
+ ## Borderless table
456
357
 
457
358
  `.borderless` removes all default borders:
458
359
 
@@ -465,19 +366,14 @@ th hr.x2 {
465
366
  {.borderless}
466
367
  </article>
467
368
 
468
- Living example: https://bladeswitch.com/#minimal-dependencies table
469
-
470
- \3c !--section--> */
471
-
369
+ \3c !--section-->*/
472
370
  table.borderless th,
473
371
  table.borderless td {
474
372
  border: none;
475
373
  }
476
-
477
374
  /*
478
375
  \3c !--section:code-->
479
376
  ```css */
480
-
481
377
  table.responsive,
482
378
  .breakout > table:not(.does-not-exist),
483
379
  .breakout-all > table:not(.does-not-exist) {
@@ -497,7 +393,6 @@ table.responsive,
497
393
  transform: translateX(-50%);
498
394
  -webkit-overflow-scrolling: touch; /* Smooth scroll for iOS */
499
395
  }
500
-
501
396
  table.responsive th,
502
397
  table.responsive td,
503
398
  .breakout > table:not(.does-not-exist) th,
@@ -506,7 +401,6 @@ table.responsive th,
506
401
  .breakout-all > table:not(.does-not-exist) td {
507
402
  padding-inline-start: 0;
508
403
  }
509
-
510
404
  /*```
511
405
  `table:not(.does-not-exist)` trick (inspired by postcss) is used here to increase specificity against selectors like `&:is(table, .table)`
512
406
 
@@ -534,25 +428,21 @@ Living examples: \3c !--A-Z-->
534
428
  - https://blades.ninja/ssg/
535
429
 
536
430
  \3c !--section--> */
537
-
538
- /* Extends https://github.com/picocss/pico/blob/main/scss/content/_code.scss
539
- \3c !--section:docs-->
540
-
541
- ### Code
431
+ /*\3c !--section:docs-->
542
432
 
543
433
  The `<pre><code>` blocks are Prism-compatible and support captions via `data-caption="..."` attribute:
544
434
 
545
- ```treeview {data-caption="Blades CSS:"}
546
- ./css/
547
- ├── blades.core.css # reusable class-light utilities, unthemed
548
- ├── blades.theme.css # minimal opinionated theme
549
- └── blades.css # above two together
435
+ ```treeview {data-caption="Blades CSS ships:"}
436
+ /css/
437
+ ├── blades.css (Pico CSS successor)
438
+ ├── blades.standalone.css (framework-agnostic)
439
+ ├── ninja.css (experimental add-ons)
440
+ └── pico.css (underlying fork)
550
441
  ```
551
- How it works:
552
- ```css */
553
442
 
443
+ \3c !--section:code-->
444
+ ```css */
554
445
  @media (width < 768px) {
555
-
556
446
  pre {
557
447
  border-radius: 0
558
448
  }
@@ -568,32 +458,44 @@ code[data-caption]::before {
568
458
  opacity: 50%;
569
459
  }
570
460
 
571
- /*```
572
- \3c !--section--> */
573
-
574
461
  /* Forms */
575
-
576
- /* Moved here from https://github.com/anyblades/float-label-css for easier maintenance
577
- \3c !--section:docs-->
462
+ /*!
463
+ * Float Label CSS v2 by Blades CSS 🥷🏷️
464
+ * Copyright 2016–2026 (https://blades.ninja/css/float-label/)
465
+ * Licensed under MIT
466
+ */
467
+ /*\3c !--section:docs-->
578
468
 
579
469
  First, we target either:
580
470
  1. `<label>` which `:has` inner form inputs (classless approach)
581
471
  2. or explicit `.has-float-label` class (explicit approach)
582
- ```css */
583
472
 
584
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select),
473
+ ```css */
474
+ label:has(> span):has(
475
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
476
+ textarea,
477
+ select
478
+ ),
585
479
  .has-float-label {
586
480
  display: block;
587
481
  position: relative;
482
+ /*```
483
+ Finally, we detect if placeholder is shown, but not in focus. That means we can safely hide it, and enlarge the float label instead:
484
+ ```css */
588
485
  }
589
-
590
- /*
591
- ```
592
- Then, we define the default/fallback state (when the float label should be minimized):
593
- ```css */
594
-
595
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) > span,
596
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) label,
486
+ /*```
487
+ Then, we define the default/fallback state (when the float label should be minimized):
488
+ ```css */
489
+ label:has(> span):has(
490
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
491
+ textarea,
492
+ select
493
+ ) > span,
494
+ label:has(> span):has(
495
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
496
+ textarea,
497
+ select
498
+ ) label,
597
499
  .has-float-label > span,
598
500
  .has-float-label label {
599
501
  position: absolute;
@@ -602,46 +504,120 @@ label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"
602
504
  font-size: 75%;
603
505
  cursor: text;
604
506
  }
605
-
606
- /*
607
- ```
608
- Finally, we detect if placeholder is shown, but not in focus. That means we can safely hide it, and enlarge the float label instead:
609
- ```css */
610
-
611
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) *:placeholder-shown:not(:focus)::-moz-placeholder, .has-float-label *:placeholder-shown:not(:focus)::-moz-placeholder {
507
+ label:has(> span):has(
508
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
509
+ textarea,
510
+ select
511
+ ) *:placeholder-shown:not(:focus)::-moz-placeholder, .has-float-label *:placeholder-shown:not(:focus)::-moz-placeholder {
612
512
  opacity: 0;
613
513
  }
614
-
615
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) *:-moz-placeholder:not(:focus)::placeholder, .has-float-label *:-moz-placeholder:not(:focus)::placeholder {
514
+ label:has(> span):has(
515
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
516
+ textarea,
517
+ select
518
+ ) *:-moz-placeholder:not(:focus)::placeholder, .has-float-label *:-moz-placeholder:not(:focus)::placeholder {
616
519
  opacity: 0;
617
520
  }
618
-
619
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) *:placeholder-shown:not(:focus)::placeholder, .has-float-label *:placeholder-shown:not(:focus)::placeholder {
521
+ label:has(> span):has(
522
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
523
+ textarea,
524
+ select
525
+ ) *:placeholder-shown:not(:focus)::placeholder, .has-float-label *:placeholder-shown:not(:focus)::placeholder {
620
526
  opacity: 0;
621
527
  }
622
-
623
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:-moz-placeholder:not(:focus)) > span, label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:-moz-placeholder:not(:focus)) label, .has-float-label:has(*:-moz-placeholder:not(:focus)) > span, .has-float-label:has(*:-moz-placeholder:not(:focus)) label {
528
+ label:has(> span):has(
529
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
530
+ textarea,
531
+ select
532
+ ):has(*:-moz-placeholder:not(:focus)) > span, label:has(> span):has(
533
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
534
+ textarea,
535
+ select
536
+ ):has(*:-moz-placeholder:not(:focus)) label, .has-float-label:has(*:-moz-placeholder:not(:focus)) > span, .has-float-label:has(*:-moz-placeholder:not(:focus)) label {
624
537
  font-size: inherit;
625
538
  opacity: 50%;
626
539
  }
627
-
628
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:placeholder-shown:not(:focus)) > span,
629
- label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:placeholder-shown:not(:focus)) label,
540
+ label:has(> span):has(
541
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
542
+ textarea,
543
+ select
544
+ ):has(*:placeholder-shown:not(:focus)) > span,
545
+ label:has(> span):has(
546
+ input:not([type="checkbox"], [type="radio"], [type="range"]),
547
+ textarea,
548
+ select
549
+ ):has(*:placeholder-shown:not(:focus)) label,
630
550
  .has-float-label:has(*:placeholder-shown:not(:focus)) > span,
631
551
  .has-float-label:has(*:placeholder-shown:not(:focus)) label {
632
552
  font-size: inherit;
633
553
  opacity: 50%;
634
554
  }
555
+ /*```
635
556
 
636
- /*
637
- ```
638
557
  The `:has(*:placeholder-shown:not(:focus))` trick allows this input state information to *propagate* to the parent level. This enables modern CSS to target inner float label (`<span>` or `<label>`) regardless of its position relative to the input field.
639
558
 
640
559
  Historically, this was not possible: the float label had to be placed after the input field to be targeted using the `input:focus + label` selector.
641
- \3c !--section--> */
560
+
561
+ */
642
562
 
643
563
  /* Components */
564
+ /* Extends https://github.com/picocss/pico/blob/main/scss/content/_link.scss
565
+ \3c !--section:code-->
566
+ ```css */
567
+ /* Use inline flex only if link contains an icon */
568
+ a:not(.does-not-exist):has(> i) {
569
+ display: inline-flex;
570
+ overflow-y: clip; /* to work in pair with text-underline-offset in Safari */
571
+ gap: 0.375ch; /* =3/8 */
572
+ text-wrap: balance;
573
+ }
574
+ a:not(.does-not-exist) > i {
575
+ float: left; /* ✅ Chrome ❌ Safari */
576
+ font-style: normal;
577
+ text-underline-offset: -2em; /* ❌ Chrome ✅ Safari - to clip it with overflow-y */
578
+ }
579
+ /* Favicons */
580
+ a:not(.does-not-exist) > i > img {
581
+ display: inline-block; /* for Tailwind CSS Typography */
582
+ max-width: none; /* to keep ratio safe */
583
+ height: 1.25em;
584
+ margin-block-start: calc(-0.25em / 2);
585
+ }
586
+ a:not(.does-not-exist) > i[class^="fa-"],
587
+ a:not(.does-not-exist) > i[class*=" fa-"] {
588
+ line-height: inherit;
589
+ --fa-width: auto;
590
+ }
591
+ /* Font Awesome */
592
+ a:not(.does-not-exist) > i.fa-lg {
593
+ line-height: normal;
594
+ }
595
+ /*```
596
+ \3c !--section:summary-->
597
+
598
+ Use *Bl*ades `<i>`-helper to wrap emojis, favicons, or simply drop Font Awesome icons inside links.
599
+ It automatically handles sizing and alignment while preventing underline on icons.
600
+
601
+ \3c !--section:docs-->
644
602
 
603
+ <article class="breakout-item">
604
+
605
+ | Use `<i>`-helper with | Clean HTML without `<span>ning` |
606
+ | ------------------------------------------------------------------------------------- | ------------------------------------ |
607
+ | [<i>🥷</i> Emojis](#) | `<a><i>🥷</i> Text</a>` |
608
+ | [<i>![](https://www.google.com/s2/favicons?domain=any.digital&sz=64)</i> Favicons](#) | `<a><i><img src="..."></i> Text</a>` |
609
+ | [<i class="fa-brands fa-github fa-lg"></i> Font Awesome icons](#) | `<a><i class="fa-..."></i> Text</a>` |
610
+ </article>
611
+
612
+ Or even for:
613
+
614
+ <article>
615
+ <a href="#" style="margin: 0 0 0 1rem"><i>🥷</i> very-very-very-very-very-<br>long-multiline-links</a>
616
+ </article>
617
+
618
+ How we made it: https://codepen.io/editor/anydigital/pen/019d2b94-5616-75dc-a23e-e111869d5237
619
+
620
+ \3c !--section--> */
645
621
  /*\3c !--section:docs-->
646
622
 
647
623
  Use `dl.timeline` or wrap a `<dl>` with `.has-timeline` to style it as a
@@ -658,21 +634,16 @@ Mar 15, 2025
658
634
  </article>
659
635
 
660
636
  \3c !--section:code-->```css */
661
-
662
637
  dl.timeline dd, .has-timeline > dl dd {
663
638
  margin-block: 0;
664
639
  padding: 0.25rem 0 0.75rem 1rem;
665
- border-inline-start: 0.25rem solid gray;
640
+ border-inline-start: 0.25rem solid var(--blades-timeline-border-color);
666
641
  }
667
642
 
668
- /*```*/
669
-
670
643
  /* Utilities */
644
+ /*\3c !--section:docs-->
671
645
 
672
- /* Extends https://github.com/picocss/pico/tree/main/scss/utilities
673
- \3c !--section:docs-->
674
-
675
- ### Auto-dark
646
+ ## Dark auto
676
647
 
677
648
  `.dark-auto` automatically creates a simple dark version of any element:
678
649
 
@@ -682,34 +653,26 @@ dl.timeline dd, .has-timeline > dl dd {
682
653
 
683
654
  How it works:
684
655
  ```css */
685
-
686
656
  /* Per https://picocss.com/docs/css-variables#css-variables-for-color-schemes */
687
-
688
657
  :root {
689
658
  --blades-dark-filter: invert(100%) hue-rotate(180deg);
690
659
  }
691
-
692
660
  .dark-auto {
693
661
  /* Dark color scheme (Forced) */
694
662
  }
695
-
696
663
  /* Dark color scheme (Auto) */
697
-
698
664
  @media (prefers-color-scheme: dark) {
699
-
700
665
  .dark-auto {
701
666
  filter: var(--blades-dark-filter)
702
667
  }
703
668
  }
704
-
705
669
  .dark-auto[data-theme="dark"],
706
670
  .dark-auto:where([data-theme="dark"] *) {
707
671
  filter: var(--blades-dark-filter);
708
672
  }
709
-
710
673
  /*```
711
674
 
712
- ### Faded
675
+ ## Faded
713
676
 
714
677
  `.faded` reduces the opacity of an element:
715
678
 
@@ -719,30 +682,21 @@ How it works:
719
682
 
720
683
  How it works:
721
684
  ```css */
722
-
723
685
  .faded {
724
686
  opacity: 50%;
725
687
  }
726
-
727
688
  .faded:hover {
728
689
  opacity: 87.5%;
729
690
  }
730
-
731
691
  /*```
732
692
  \3c !--section--> */
733
693
 
734
694
  /* Fix the scrollbar color when inverted by https://tailwindcss.com/docs/filter-invert */
735
-
736
695
  .invert ::-webkit-scrollbar {
737
696
  filter: invert(1) !important;
738
697
  }
739
-
740
- /*
741
- \3c !--section:code-->
742
- ```css */
743
-
698
+ /*\3c !--section:code-->```css */
744
699
  /* Default/fallback state */
745
-
746
700
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) > span,
747
701
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) label,
748
702
  .has-float-label > span,
@@ -752,7 +706,6 @@ label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"
752
706
  opacity: 75%;
753
707
  transition: all 0.25s;
754
708
  }
755
-
756
709
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) input,
757
710
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) textarea,
758
711
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) select,
@@ -763,24 +716,19 @@ label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"
763
716
  padding-inline-start: 1rem; /* match Pico */
764
717
  padding-block: 1.125rem 0.375rem; /* match Pico's total: 2 x 0.75rem = 1.5rem */
765
718
  }
766
-
767
719
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) input::-moz-placeholder, label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) textarea::-moz-placeholder, label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) select::-moz-placeholder, .has-float-label input::-moz-placeholder, .has-float-label textarea::-moz-placeholder, .has-float-label select::-moz-placeholder {
768
720
  opacity: 100%;
769
721
  -moz-transition: all 0.25s;
770
722
  transition: all 0.25s;
771
723
  }
772
-
773
724
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) input::placeholder, label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) textarea::placeholder, label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) select::placeholder, .has-float-label input::placeholder, .has-float-label textarea::placeholder, .has-float-label select::placeholder {
774
725
  opacity: 100%;
775
726
  transition: all 0.25s;
776
727
  }
777
-
778
728
  /* Enlarged state */
779
-
780
729
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:-moz-placeholder:not(:focus)) > span, label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:-moz-placeholder:not(:focus)) label, .has-float-label:has(*:-moz-placeholder:not(:focus)) > span, .has-float-label:has(*:-moz-placeholder:not(:focus)) label {
781
730
  padding-block: 0.75rem; /* match Pico */
782
731
  }
783
-
784
732
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:placeholder-shown:not(:focus)) > span,
785
733
  label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select):has(*:placeholder-shown:not(:focus)) label,
786
734
  .has-float-label:has(*:placeholder-shown:not(:focus)) > span,
@@ -841,7 +789,6 @@ h5,
841
789
  h6 {
842
790
  text-wrap: balance;
843
791
  }
844
-
845
792
  h1 {
846
793
  margin-bottom: 1rem; /* for tw-typography */
847
794
  font-size: 2.5em; /* for pico.css & tw-typography */
@@ -886,15 +833,15 @@ table pre {
886
833
  margin-bottom: 0.25rem;
887
834
  }
888
835
 
889
- [data-jump-to="top"] {
836
+ [href="#top"] {
890
837
  opacity: 25%;
891
838
  }
892
839
 
893
- [data-jump-to="top"]:hover {
840
+ [href="#top"]:hover {
894
841
  opacity: 75%;
895
842
  }
896
843
 
897
- [data-jump-to="top"] > i {
844
+ [href="#top"] > i {
898
845
  display: inline-block;
899
846
  margin: 0.5rem;
900
847
  padding: 0.25rem 0.375rem;
@@ -913,6 +860,3 @@ table pre {
913
860
  .faded a {
914
861
  text-decoration-style: dotted;
915
862
  }
916
-
917
- /*```
918
- \3c !--section--> */