@anyblades/blades 3.1.0-beta.2 → 3.1.0-beta.5

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 (47) hide show
  1. package/README.md +21 -18
  2. package/css/blades.css +148 -1
  3. package/css/blades.min.css +4 -174
  4. package/css/blades.ninja.css +148 -1
  5. package/css/blades.ninja.min.css +4 -182
  6. package/css/float-label.min.css +2 -51
  7. package/css/layered/blades.layered.css +148 -1
  8. package/css/layered/blades.layered.min.css +4 -174
  9. package/css/layered/components/dropdown-popover.css +107 -0
  10. package/css/layered/components/loading.css +19 -1
  11. package/css/layered/components/modal-modern.css +28 -0
  12. package/css/layered/components/timeline.css +24 -0
  13. package/css/layered/components.css +18 -1
  14. package/css/layered/content/code-block.css +32 -0
  15. package/css/layered/content/icon.css +60 -0
  16. package/css/layered/forms/float-label/core.css +103 -0
  17. package/css/layered/forms/float-label/theme.css +110 -0
  18. package/css/layered/forms/float-label.css +210 -0
  19. package/css/layered/layout/columns.css +30 -0
  20. package/css/layered/variables-blades.css +23 -0
  21. package/css/pico.conditional.css +18 -1
  22. package/css/pico.conditional.min.css +2 -111
  23. package/css/pico.css +18 -1
  24. package/css/pico.min.css +2 -111
  25. package/package.json +7 -7
  26. package/src/blades/_index.css +11 -6
  27. package/src/blades/components/_dropdown-popover.css +107 -0
  28. package/src/blades/components/_modal-modern.css +26 -0
  29. package/src/blades.css +0 -1
  30. package/src/float-label.css +1 -1
  31. package/src/ninja/LICENSE +22 -0
  32. package/src/ninja/README.md +1 -0
  33. package/src/ninja/_index.css +1 -1
  34. package/src/pico/components/_loading.css +27 -1
  35. package/_includes/blades/links.liquid +0 -21
  36. package/_includes/blades/links.njk +0 -9
  37. package/_includes/blades/links.twig +0 -18
  38. /package/src/{_compact.css → blades/_compact.css} +0 -0
  39. /package/src/blades/{_variables.css → _variables-blades.css} +0 -0
  40. /package/src/blades/{_timeline.css → components/_timeline.css} +0 -0
  41. /package/src/blades/{_code.css → content/_code-block.css} +0 -0
  42. /package/src/blades/{_icon.css → content/_icon.css} +0 -0
  43. /package/src/blades/{float-label → forms/float-label}/_core.css +0 -0
  44. /package/src/blades/{float-label → forms/float-label}/_index.css +0 -0
  45. /package/src/blades/{float-label → forms/float-label}/_theme.css +0 -0
  46. /package/src/blades/{_columns.css → layout/_columns.css} +0 -0
  47. /package/src/ninja/{_page-anchors.css → _scroll-to.css} +0 -0
package/README.md CHANGED
@@ -1,21 +1,29 @@
1
- # 🥷 *Bl*ades  ![][NPM_V]
1
+ # blades<sup>✨</sup>
2
2
 
3
- [NPM_V]: https://img.shields.io/npm/v/@anyblades/blades?label=&color=darkslategray
3
+ ![][V] ![][NEXT_V] ![][LIC] [![][X]][X_URL] [![][B]][B_URL] [![][M]][M_URL]
4
+
5
+ [V]: https://img.shields.io/npm/v/@anyblades/blades?label=&color=brightgreen
6
+ [NEXT_V]: https://img.shields.io/github/v/release/anyblades/blades?include_prereleases&label=next&color=blue
7
+ [LIC]: https://img.shields.io/badge/license-MIT-blue
8
+ [X]: https://img.shields.io/twitter/follow/BladesCSS?label=Twitter
9
+ [X_URL]: https://x.com/BladesCSS
10
+ [B]: https://img.shields.io/bluesky/followers/blades.dev?label=
11
+ [B_URL]: https://bsky.app/profile/blades.dev
12
+ [M]: https://img.shields.io/mastodon/follow/117487235444867518?label=NEW
13
+ [M_URL]: https://mastodon.social/@BladesCSS
4
14
 
5
15
  <!--section:summary-->
6
16
 
7
17
  <h1><span class="mark-brand">Minimal CSS Framework</span> <small>for</small> Semantic HTML</h1>
8
18
 
9
- <big>Fully compatible and actively maintained successor to [Pico CSS](https://picocss.com/).</big>
19
+ <big>Blades CSS is a modern drop&#8209;in successor to [Pico CSS](//picocss.com/docs/maintenance).</big>
10
20
 
11
- Simply switch `pico.css` to `blades.css` OR use `blades.standalone.css` with other frameworks.
21
+ Just switch `pico.css` to `blades.css` — and voilà!
12
22
 
13
23
  <nav>
14
- <a href="//blades.ninja/css/" role="button">Get started <i>✨</i></a>
15
- <b hidden>|</b>
16
- <a href="//blades.ninja/css/standalone/" role="button" class="outline">
17
- Standalone <i><i class="fa-brands fa-tailwind-css"></i><i class="fa-brands fa-bootstrap"></i></i>
18
- </a>
24
+ <a href="//blades.dev/css/" role="button">Get started</a>
25
+ <b hidden>&nbsp;•&nbsp;</b>
26
+ <a href="//blades.dev/css/pico/" role="button" class="outline">Inspired by Pico ✨</a>
19
27
  </nav>
20
28
 
21
29
  <!--section:gh-only-->
@@ -117,8 +125,9 @@ import "@anyblades/blades/layered/components/modal";
117
125
  ```
118
126
 
119
127
  - Bundlers dedupe JS-side CSS imports and split them per route, so each part is downloaded and cached once and only where it's used.
128
+ - Two layered `components/*` parts also carry Blades' own additions, as in `blades.css`: `components/modal` includes the CSS-only open animation for `showModal()`, and `components/dropdown-popover` styles `<ul popover>` / `<menu popover>` menus opened by a button.
120
129
  - Override in your component's `<style>` or `app.css`, or set `--pico-*` variables on `:root` or on an element. Unlayered CSS always wins over `@layer blades`. The usual layer caveats apply: Blades' own `!important` declarations beat yours, and if you use other layered CSS, declare the order once, before anything else is loaded. For Tailwind v4 that is `@layer theme, base, blades, components, utilities;` at the top of the CSS file you import first (before `@import "tailwindcss"`), and that file has to be imported before any `layered/*` part, because the first `@layer` the browser sees fixes the order.
121
- - Parts don't `@import` the parts they build on (e.g. `components/modal` expects `components/card`, `components/group` and `components/dropdown` expect `content/button` and `forms/basics`, and the `forms/*` extras expect `forms/basics`). If they did, every route chunk would carry its own copy. Import those shared parts once, at the root or next to the part that needs them; bundlers dedupe the imports.
130
+ - Parts don't `@import` the parts they build on (e.g. `components/modal` expects `components/card`, `components/group` and `components/dropdown` expect `content/button` and `forms/basics`, `components/dropdown-popover` expects `components/dropdown`, and the `forms/*` extras expect `forms/basics`). If they did, every route chunk would carry its own copy. Import those shared parts once, at the root or next to the part that needs them; bundlers dedupe the imports.
122
131
  - An `@import` inside a scoped `<style>` (Svelte, Astro, Vue `scoped`) is processed as that component's own styles. The rules get scoped to the component's markup (Svelte also prunes the unused ones) and are duplicated in every component that imports them.
123
132
  - Landmark padding: `pico.css` and `blades.ninja.css` pad `body > header`, `body > main` and `body > footer` like Pico, which doesn't match when the framework wraps the page in an element (SvelteKit's `<div style="display: contents">`, Next.js pages router `#__next`, Nuxt `#__nuxt`, a SPA's `#root` / `#app`). `blades.css` doesn't pad them; with per-part imports, add `layout/landmarks` (or `layered/layout/landmarks`) for Pico's `body >` padding.
124
133
 
@@ -290,6 +299,7 @@ Add `<html data-compact>` for the whole page, or put the attribute on a single e
290
299
  ## <sup style>Featured by</sup> <!-- [1] big projects [2] awesome [3] smaller projects [4] blogs/social -->
291
300
 
292
301
  - [getgrav.org](https://getgrav.org/migrate-to-2#:~:text=blades)
302
+ - https://usehadron.dev/
293
303
  - [@11ty/buildawesome](https://bsky.app/profile/11ty.dev/post/3mp75r53ccv2b) [↗](https://mastodon.social/@11ty@neighborhood.11ty.dev/116816881367741413)
294
304
  - [<i class="fa-brands fa-build-awesome mark-brand"></i> buildawesome.one](https://buildawesome.one/#:~:text=blades)
295
305
  - https://github.com/dbohdan/classless-css
@@ -297,7 +307,7 @@ Add `<html data-compact>` for the whole page, or put the attribute on a single e
297
307
  - [/gabrielizalo/awesome-css-frameworks](https://github.com/gabrielizalo/awesome-css-frameworks-and-ui-libraries/tree/master/Lightweight)
298
308
  - https://github.com/eggplantpasta/greenroom
299
309
  - [/adamdjbrett/11ty-wisp](https://github.com/adamdjbrett/11ty-wisp#features:~:text=blades) [↗](https://github.com/adamdjbrett/000000076#:~:text=blades)
300
- - [doany.io](https://doany.io/en/posts/svelte-bun-sqlite/#on-biome-and-css)
310
+ - [doany.io](https://doany.io/en/posts/svelte-bun-sqlite/#on-biome-and-css:~:text=anyblades)
301
311
  - [@box464](https://mastodon.social/@box464/117442493473921346)
302
312
  - [@draupnerdata](https://www.youtube.com/watch?v=yDjArj9HSjs&t=180s)
303
313
  - [@cantdutchthis](https://news.ycombinator.com/item?id=48729467)
@@ -309,10 +319,3 @@ Add `<html data-compact>` for the whole page, or put the attribute on a single e
309
319
 
310
320
  <!--{.markerless .columns}-->
311
321
  <!--[hostfurl.com, @johnheenan]-->
312
-
313
- ## <sup style>Credits</sup>
314
-
315
- - https://picocss.com/ for [inspiration](//blades.ninja/css/pico/)
316
- - https://11ty.dev/ for [site generation](//buildawesome.one/)
317
-
318
- <!--{.markerless}-->
package/css/blades.css CHANGED
@@ -2403,7 +2403,11 @@ label > details.dropdown {
2403
2403
  --pico-loading-spinner-opacity: 0.5;
2404
2404
  }
2405
2405
 
2406
- [aria-busy="true"]:not(input, select, textarea, html, form) {
2406
+ [aria-busy="true"]:not(input, select, textarea, html, form):not(
2407
+ :where(button, [type="submit"], [type="button"], [type="reset"], [role="button"])
2408
+ ) {
2409
+ /* :where() zeroes the specificity of the button list inside :not(), so this rule stays
2410
+ * low-specificity and doesn't override the more specific button rules below */
2407
2411
  white-space: nowrap;
2408
2412
  }
2409
2413
  [aria-busy="true"]:not(input, select, textarea, html, form)::before {
@@ -2432,6 +2436,19 @@ a[aria-busy="true"] {
2432
2436
  pointer-events: none;
2433
2437
  }
2434
2438
 
2439
+ /* Buttons keep their size: the label stays in place but hidden, the spinner sits on top */
2440
+ button[aria-busy="true"]:not(:empty), [type="submit"][aria-busy="true"]:not(:empty), [type="button"][aria-busy="true"]:not(:empty), [type="reset"][aria-busy="true"]:not(:empty), [role="button"][aria-busy="true"]:not(:empty) {
2441
+ position: relative;
2442
+ color: transparent;
2443
+ }
2444
+ button[aria-busy="true"]:not(:empty) > *, [type="submit"][aria-busy="true"]:not(:empty) > *, [type="button"][aria-busy="true"]:not(:empty) > *, [type="reset"][aria-busy="true"]:not(:empty) > *, [role="button"][aria-busy="true"]:not(:empty) > * {
2445
+ opacity: 0;
2446
+ }
2447
+ button[aria-busy="true"]:not(:empty)::before, [type="submit"][aria-busy="true"]:not(:empty)::before, [type="button"][aria-busy="true"]:not(:empty)::before, [type="reset"][aria-busy="true"]:not(:empty)::before, [role="button"][aria-busy="true"]:not(:empty)::before {
2448
+ position: absolute;
2449
+ margin: auto;
2450
+ inset: 0;
2451
+ }
2435
2452
  /**
2436
2453
  * Modal (<dialog>)
2437
2454
  */
@@ -3281,6 +3298,31 @@ label:has(> span):has(
3281
3298
  padding-block: 0.75rem; /* match Pico */
3282
3299
  }
3283
3300
  /* Components */
3301
+ /*\3c !--section:docs-->
3302
+
3303
+ A `<dialog>` opened with `dialog.showModal()` fades in and its `<article>` zooms in, without JS classes.
3304
+ Pico's modal JS (`.modal-is-open`, `.modal-is-opening`, `.modal-is-closing`) still works on top of it.
3305
+
3306
+ - `dialog.close()` closes it at once, as before.
3307
+ - `<dialog open>` in markup and non-modal `dialog.show()` aren't animated.
3308
+ - Browsers without `@starting-style`, and reduced motion, open it at once.
3309
+
3310
+ \3c !--section:code-->```css */
3311
+ dialog:modal {
3312
+ transition: opacity var(--pico-transition);
3313
+ }
3314
+ dialog:modal > article {
3315
+ transition: transform var(--pico-transition);
3316
+ }
3317
+ @starting-style {
3318
+ dialog:modal {
3319
+ opacity: 0;
3320
+ }
3321
+
3322
+ dialog:modal > article {
3323
+ transform: scale(0.96);
3324
+ }
3325
+ }
3284
3326
  /* Extends https://github.com/picocss/pico/blob/main/scss/content/_link.scss
3285
3327
  \3c !--section:code-->
3286
3328
  ```css */
@@ -3340,6 +3382,110 @@ How we made it: https://codepen.io/editor/anydigital/pen/019d2b94-5616-75dc-a23e
3340
3382
  \3c !--section--> */
3341
3383
  /*\3c !--section:docs-->
3342
3384
 
3385
+ A `<ul>` or `<menu>` with the `popover` attribute is styled like a dropdown and opens next to the button
3386
+ that toggles it, with no JS:
3387
+
3388
+ ```html
3389
+ <button popovertarget="actions">Actions</button>
3390
+ <ul id="actions" popover>
3391
+ <li><a href="#">Edit</a></li>
3392
+ <li><a href="#">Duplicate</a></li>
3393
+ </ul>
3394
+ ```
3395
+
3396
+ - Clicking outside or pressing Esc closes it, and it's drawn on top of everything (no `z-index`, no clipping by `overflow`).
3397
+ - It opens below the button (on the reading side, also in RTL) and flips when there's no room.
3398
+ Open it with `popovertarget`, `commandfor` or `showPopover({ source })`, so it knows its button.
3399
+ - Browsers without CSS anchor positioning show it in the middle of the screen instead, like a plain popover.
3400
+ - It uses the dropdown colours (`--pico-dropdown-*`, from `components/dropdown`).
3401
+
3402
+ \3c !--section:code-->```css */
3403
+ :is(ul, menu)[popover] {
3404
+ min-width: -moz-fit-content;
3405
+ min-width: fit-content;
3406
+ margin: auto;
3407
+ padding: 0;
3408
+ border: var(--pico-border-width) solid var(--pico-dropdown-border-color);
3409
+ border-radius: var(--pico-border-radius);
3410
+ background-color: var(--pico-dropdown-background-color);
3411
+ box-shadow: var(--pico-dropdown-box-shadow);
3412
+ color: var(--pico-dropdown-color);
3413
+ white-space: nowrap;
3414
+ opacity: 0;
3415
+ transition:
3416
+ opacity var(--pico-transition),
3417
+ overlay var(--pico-transition) allow-discrete,
3418
+ display var(--pico-transition) allow-discrete;
3419
+ }
3420
+ /* Beats `nav ul` / `aside ul`, which would otherwise show the closed menu */
3421
+ :is(ul, menu)[popover]:not(:popover-open) {
3422
+ display: none;
3423
+ }
3424
+ :is(ul, menu)[popover]:popover-open {
3425
+ display: block;
3426
+ opacity: 1;
3427
+ }
3428
+ :is(ul, menu)[popover] li {
3429
+ display: block;
3430
+ margin-bottom: 0;
3431
+ padding: calc(var(--pico-form-element-spacing-vertical) * 0.5)
3432
+ var(--pico-form-element-spacing-horizontal);
3433
+ list-style: none;
3434
+ }
3435
+ :is(ul, menu)[popover] li:first-of-type {
3436
+ margin-top: calc(var(--pico-form-element-spacing-vertical) * 0.5);
3437
+ }
3438
+ :is(ul, menu)[popover] li:last-of-type {
3439
+ margin-bottom: calc(var(--pico-form-element-spacing-vertical) * 0.5);
3440
+ }
3441
+ :is(ul, menu)[popover] li:has(label):hover {
3442
+ background-color: var(--pico-dropdown-hover-background-color);
3443
+ }
3444
+ :is(ul, menu)[popover] li a {
3445
+ display: block;
3446
+ margin: calc(var(--pico-form-element-spacing-vertical) * -0.5)
3447
+ calc(var(--pico-form-element-spacing-horizontal) * -1);
3448
+ padding: calc(var(--pico-form-element-spacing-vertical) * 0.5)
3449
+ var(--pico-form-element-spacing-horizontal);
3450
+ overflow: hidden;
3451
+ border-radius: 0;
3452
+ color: var(--pico-dropdown-color);
3453
+ text-decoration: none;
3454
+ text-overflow: ellipsis;
3455
+ }
3456
+ :is(ul, menu)[popover] li a:is(:hover, :focus, :active, :focus-visible, [aria-current]:not([aria-current="false"])) {
3457
+ background-color: var(--pico-dropdown-hover-background-color);
3458
+ }
3459
+ :is(ul, menu)[popover] li label {
3460
+ width: 100%;
3461
+ }
3462
+ /* Beats Pico's `nav ul:first-of-type` / `:last-of-type` margins whatever the load order (soft win via :not) */
3463
+ nav :is(ul, menu)[popover]:not(.does-not-exist) {
3464
+ margin: auto;
3465
+ }
3466
+ @starting-style {
3467
+ :is(ul, menu)[popover]:popover-open {
3468
+ opacity: 0;
3469
+ }
3470
+ }
3471
+
3472
+ /* Next to the button that opened it (its implicit anchor) */
3473
+ @supports (position-area: bottom) {
3474
+ :is(ul, menu)[popover],
3475
+ nav :is(ul, menu)[popover]:not(.does-not-exist) {
3476
+ position-area: bottom span-right;
3477
+ position-try-fallbacks: flip-block, flip-inline;
3478
+ margin: 0;
3479
+ margin-top: var(--pico-outline-width);
3480
+ inset: auto;
3481
+ }
3482
+
3483
+ :is(ul, menu)[popover]:dir(rtl), nav :is(ul, menu)[popover]:not(.does-not-exist):dir(rtl) {
3484
+ position-area: bottom span-left;
3485
+ }
3486
+ }
3487
+ /*\3c !--section:docs-->
3488
+
3343
3489
  Use `dl.timeline` or wrap a `<dl>` with `.has-timeline` to style it as a
3344
3490
  vertical timeline — each `<dd>` gets a left border strip acting as the track:
3345
3491
 
@@ -3359,6 +3505,7 @@ dl.timeline dd, .has-timeline > dl dd {
3359
3505
  padding: 0.25rem 0 0.75rem 1rem;
3360
3506
  border-inline-start: 0.25rem solid var(--blades-timeline-border-color);
3361
3507
  }
3508
+ /* Compact version (opt-in via `data-compact` attr anywhere) */
3362
3509
  /*
3363
3510
  * Compact: opt-in denser sizing and a more current look, applied under `data-compact` on
3364
3511
  * <html> (whole page) or on any element such as a single <form>. Without the attribute it