@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.
- package/README.md +21 -18
- package/css/blades.css +148 -1
- package/css/blades.min.css +4 -174
- package/css/blades.ninja.css +148 -1
- package/css/blades.ninja.min.css +4 -182
- package/css/float-label.min.css +2 -51
- package/css/layered/blades.layered.css +148 -1
- package/css/layered/blades.layered.min.css +4 -174
- package/css/layered/components/dropdown-popover.css +107 -0
- package/css/layered/components/loading.css +19 -1
- package/css/layered/components/modal-modern.css +28 -0
- package/css/layered/components/timeline.css +24 -0
- package/css/layered/components.css +18 -1
- package/css/layered/content/code-block.css +32 -0
- package/css/layered/content/icon.css +60 -0
- package/css/layered/forms/float-label/core.css +103 -0
- package/css/layered/forms/float-label/theme.css +110 -0
- package/css/layered/forms/float-label.css +210 -0
- package/css/layered/layout/columns.css +30 -0
- package/css/layered/variables-blades.css +23 -0
- package/css/pico.conditional.css +18 -1
- package/css/pico.conditional.min.css +2 -111
- package/css/pico.css +18 -1
- package/css/pico.min.css +2 -111
- package/package.json +7 -7
- package/src/blades/_index.css +11 -6
- package/src/blades/components/_dropdown-popover.css +107 -0
- package/src/blades/components/_modal-modern.css +26 -0
- package/src/blades.css +0 -1
- package/src/float-label.css +1 -1
- package/src/ninja/LICENSE +22 -0
- package/src/ninja/README.md +1 -0
- package/src/ninja/_index.css +1 -1
- package/src/pico/components/_loading.css +27 -1
- package/_includes/blades/links.liquid +0 -21
- package/_includes/blades/links.njk +0 -9
- package/_includes/blades/links.twig +0 -18
- /package/src/{_compact.css → blades/_compact.css} +0 -0
- /package/src/blades/{_variables.css → _variables-blades.css} +0 -0
- /package/src/blades/{_timeline.css → components/_timeline.css} +0 -0
- /package/src/blades/{_code.css → content/_code-block.css} +0 -0
- /package/src/blades/{_icon.css → content/_icon.css} +0 -0
- /package/src/blades/{float-label → forms/float-label}/_core.css +0 -0
- /package/src/blades/{float-label → forms/float-label}/_index.css +0 -0
- /package/src/blades/{float-label → forms/float-label}/_theme.css +0 -0
- /package/src/blades/{_columns.css → layout/_columns.css} +0 -0
- /package/src/ninja/{_page-anchors.css → _scroll-to.css} +0 -0
package/README.md
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
#
|
|
1
|
+
# blades<sup>✨</sup>
|
|
2
2
|
|
|
3
|
-
[
|
|
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>
|
|
19
|
+
<big>Blades CSS is a modern drop‑in successor to [Pico CSS](//picocss.com/docs/maintenance).</big>
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
Just switch `pico.css` to `blades.css` — and voilà!
|
|
12
22
|
|
|
13
23
|
<nav>
|
|
14
|
-
<a href="//blades.
|
|
15
|
-
<b hidden
|
|
16
|
-
<a href="//blades.
|
|
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> • </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
|