barua-ui 0.3.0 → 0.3.2

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.
@@ -33,7 +33,36 @@
33
33
  }
34
34
  .b-card__title { font-size: var(--b-text-headline); font-weight: var(--b-weight-semibold); }
35
35
  .b-card__subtitle { font-size: var(--b-text-footnote); color: var(--b-text-secondary); }
36
- .b-card__media { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
36
+ /*
37
+ * The media slot works two ways and only one of them used to.
38
+ *
39
+ * When the slot IS the picture — <img class="b-card__media"> — object-fit
40
+ * crops it and all is well. When it WRAPS one, which is what a card with a
41
+ * zoom button or an async image needs, object-fit does nothing: it only
42
+ * applies to replaced elements. The child then rendered at its natural size
43
+ * and burst out of the card, and anything positioned absolutely inside it
44
+ * escaped to the page, because there was no positioned ancestor to hold it.
45
+ */
46
+ .b-card__media {
47
+ position: relative;
48
+ width: 100%;
49
+ aspect-ratio: 16 / 9;
50
+ overflow: hidden;
51
+ object-fit: cover;
52
+ }
53
+ /* Direct children fill the slot; the picture fills whatever wraps it, at any
54
+ depth, because a zoom button or an async-image span usually sits between. */
55
+ .b-card__media > * {
56
+ display: block;
57
+ width: 100%;
58
+ height: 100%;
59
+ }
60
+ .b-card__media :is(img, video, picture) {
61
+ display: block;
62
+ width: 100%;
63
+ height: 100%;
64
+ object-fit: cover;
65
+ }
37
66
  .b-card__eyebrow {
38
67
  font-size: var(--b-text-caption);
39
68
  font-weight: var(--b-weight-semibold);
@@ -232,6 +232,34 @@
232
232
  opacity: 0;
233
233
  transition: opacity var(--b-duration-slow) var(--b-ease-out);
234
234
  }
235
+ /* ---- Blur-up ------------------------------------------------------------
236
+ *
237
+ * A shimmer says "something is coming". A blurred copy of the actual picture
238
+ * says "this is what is coming", which is a different and better promise —
239
+ * the layout is right, the colours are right, and the photograph resolves
240
+ * rather than appearing.
241
+ *
242
+ * The placeholder is a thumbnail small enough to inline: a 32px-wide JPEG is
243
+ * around a kilobyte, so it costs nothing and arrives with the markup. Give
244
+ * it as a data URI on --b-img-placeholder and mark the host with
245
+ * data-placeholder, which is what tells the shimmer to stand down.
246
+ */
247
+ .b-async-img[data-placeholder]::before { display: none; }
248
+
249
+ .b-async-img[data-placeholder]::after {
250
+ content: "";
251
+ position: absolute;
252
+ inset: 0;
253
+ background: var(--b-img-placeholder) center / cover no-repeat;
254
+ /* Scaled up because a blur samples past the edges and would otherwise
255
+ leave a soft border all the way round. */
256
+ filter: blur(14px);
257
+ transform: scale(1.1);
258
+ transition: opacity var(--b-duration-slow) var(--b-ease-out);
259
+ pointer-events: none;
260
+ }
261
+ .b-async-img[data-placeholder].is-loaded::after { opacity: 0; }
262
+
235
263
  .b-async-img.is-loaded::before { display: none; }
236
264
  .b-async-img.is-loaded img { opacity: 1; }
237
265
 
package/dist/index.cjs CHANGED
@@ -2395,7 +2395,32 @@ var OnboardingFeature = (0, import_react19.forwardRef)(
2395
2395
  var import_react20 = require("react");
2396
2396
  var import_jsx_runtime20 = require("react/jsx-runtime");
2397
2397
  var MediaPlaceholder = block("div", "b-media-placeholder", "MediaPlaceholder");
2398
- var AsyncImage = block("span", "b-async-img", "AsyncImage");
2398
+ var AsyncImage = (0, import_react20.forwardRef)(function AsyncImage2({ src, alt, placeholder, aspect, className, style, ...rest }, ref) {
2399
+ const [loaded, setLoaded] = (0, import_react20.useState)(false);
2400
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2401
+ "span",
2402
+ {
2403
+ className: cn("b-async-img", loaded && "is-loaded", className),
2404
+ "data-placeholder": placeholder ? "" : void 0,
2405
+ style: {
2406
+ ...aspect ? { aspectRatio: aspect } : null,
2407
+ ...placeholder ? { ["--b-img-placeholder"]: `url("${placeholder}")` } : null,
2408
+ ...style
2409
+ },
2410
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2411
+ "img",
2412
+ {
2413
+ ref,
2414
+ src,
2415
+ alt,
2416
+ onLoad: () => setLoaded(true),
2417
+ onError: () => setLoaded(true),
2418
+ ...rest
2419
+ }
2420
+ )
2421
+ }
2422
+ );
2423
+ });
2399
2424
  var Web = block("div", "b-web", "Web");
2400
2425
  var Video = block("video", "b-video", "Video");
2401
2426
  var Audio = block("div", "b-audio", "Audio");