@getvitops/core 8.0.0 → 8.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @getvitops/core
2
2
 
3
+ ## 8.0.1
4
+
3
5
  ## 8.0.0
4
6
 
5
7
  ### Major Changes
@@ -70,18 +70,31 @@ dialog.lightbox-dialog {
70
70
 
71
71
  .lightbox-dialog__track {
72
72
  max-inline-size: 100%;
73
+ /* Inert: `.carousel`'s implicit grid row is `auto`, so this percentage is
74
+ cyclic (CSS Sizing 3 §5.2) — it resolves against the row's own
75
+ content-derived size, i.e. against itself, and can never actually clamp
76
+ it. Kept only to state intent; the `<img>`/`<picture>` cap below is what
77
+ does the real work by capping the thing with an intrinsic size instead
78
+ of trying to cap a box that is sized BY that intrinsic size. */
73
79
  max-block-size: 100%;
74
80
 
81
+ /* One source of truth for the slide's breathing room: the padding below
82
+ and the image's block cap both read these, so they cannot drift apart. */
83
+ --lightbox-pad-block: 5dvh;
84
+ --lightbox-pad-inline: 5dvw;
85
+
75
86
  /* `.carousel__media`'s own rule reads this for its `object-fit` (default
76
87
  `cover`, right for a thumbnail strip, wrong for a full image — nothing
77
88
  here should be cropped). Nesting under `dialog.lightbox-dialog` gives
78
89
  this whole block higher specificity than carousel.css's bare
79
- `.carousel__media` rule regardless of import order, so it always wins. */
90
+ `.carousel__media` rule regardless of import order, so it always wins.
91
+ Belt-and-braces only now: the image rule below sizes the box to the
92
+ image, so there's normally nothing left for `object-fit` to do. */
80
93
  --carousel-media-fit: contain;
81
94
 
82
- .carousel__track {
83
- max-block-size: 100%;
84
- }
95
+ /* No `.carousel__track { max-block-size: 100% }` override here — same
96
+ cyclic-percentage trap as above, against `.carousel`'s own implicit
97
+ `auto` row. It has never applied. */
85
98
 
86
99
  /* Breathing room against the edge-to-edge dialog, on the SLIDE rather than
87
100
  the media box — a slide is the grid item `align-items: stretch` fills to
@@ -89,21 +102,31 @@ dialog.lightbox-dialog {
89
102
  glass instead of the media box's aspect-ratio doing it. */
90
103
  .carousel__slide {
91
104
  box-sizing: border-box;
92
- padding: 5vh 5vw;
105
+ padding: var(--lightbox-pad-block) var(--lightbox-pad-inline);
106
+
107
+ /* carousel.css gives a slide `align-content: start` so a `cover`ed media
108
+ box (which fills the row) hugs the top. Here the media box is
109
+ deliberately SMALLER than the slide — it's sized to the image, not the
110
+ other way around — so it must be centred instead, or a 16/9 image
111
+ would hang from the top of a row sized by a 9/16 neighbour. */
112
+ align-content: center;
93
113
  }
94
114
 
95
115
  .carousel__media {
96
- /* Full images vary wildly in ratio; let each fill its slide instead of
97
- carousel.css's fixed 16/9 box (right for a thumbnail strip, wrong
98
- here). */
116
+ /* Full images vary wildly in ratio; drop carousel.css's fixed 16/9 box
117
+ (right for a thumbnail strip, wrong here) and let the image below
118
+ determine the box's size. carousel.css's `block-size: 100%` is
119
+ deliberately NOT reinstated here: against this slide's content-sized
120
+ `auto` row it's the same cyclic percentage as the caps above, and it
121
+ only ever resolved back to the content height it was meant to cap. */
99
122
  aspect-ratio: unset;
100
- block-size: 100%;
101
123
 
102
124
  /* Painted the instant a slide is visible, whether or not the full image
103
125
  has loaded yet — the morph target is never an empty box. Set per-slide
104
126
  by `<Gallery />` from the thumbnail URL; declared as a var rather than
105
127
  a bare property so a slide that omits a thumb (no placeholder) simply
106
- paints nothing extra. */
128
+ paints nothing extra. `contain` + `center` lands on the same rect the
129
+ image below resolves to, so the placeholder doesn't jump on load. */
107
130
  background-image: var(--lightbox-placeholder, none);
108
131
  background-size: contain;
109
132
  background-position: center;
@@ -118,6 +141,42 @@ dialog.lightbox-dialog {
118
141
  a `view-transition-name` on the image here statically. */
119
142
  }
120
143
 
144
+ /* THE FIX. carousel.css sizes the media to fill its box
145
+ (`inline-size`/`block-size: 100%`) so `object-fit: cover` has something
146
+ to crop — right for a thumbnail strip, backwards for a lightbox, where
147
+ the BOX should instead be sized to the image. With both axes `auto` and
148
+ both maxes set, the browser's replaced-element min/max algorithm
149
+ (CSS 2.1 §10.4) does a ratio-preserving clamp against whichever axis
150
+ binds — so the element's border box IS the visible image, with no
151
+ cyclic percentages and no dependency on how many rows the grid above
152
+ ends up with. That also matters for two things beyond sizing: it's the
153
+ `view-transition-name` target (WCGallery.ts), so the morph must not
154
+ land on an oversized box; and it keeps the letterbox area click-through
155
+ for the dialog's `closedby="any"`.
156
+
157
+ A DESCENDANT selector, not a direct-child `>`: an `ImageMetadata` src
158
+ renders `<picture><img></picture>` (ResponsiveImage.astro), and
159
+ carousel.css's own rule only reaches the non-replaced `<picture>` via
160
+ `> :is(img, picture, …)` — `object-fit` is a no-op there, and the real
161
+ `<img>` inside gets no sizing at all. This selector covers both the
162
+ bare-`<img>` demo and the `<picture>`-wrapped case. */
163
+ .carousel__media :is(img, picture) {
164
+ /* Undo carousel.css's `inline-size/block-size: 100%` — a fixed size on
165
+ either axis would defeat the ratio-preserving clamp below. */
166
+ inline-size: auto;
167
+ block-size: auto;
168
+ max-inline-size: 100%; /* of the padded slide, i.e. ~90dvw */
169
+ /* The one cap that actually binds (not a percentage, so not cyclic).
170
+ `--lightbox-caption-space` is 0 by default — nothing here uses a
171
+ caption today, but `<figcaption>` stacks below the image inside this
172
+ same box (Gallery.astro), so a captioned gallery must set it to
173
+ reserve room or the slide can exceed the viewport by that height. */
174
+ max-block-size: calc(
175
+ 100dvh - 2 * var(--lightbox-pad-block) - var(--lightbox-caption-space, 0px)
176
+ );
177
+ margin-inline: auto; /* centre in the full-width figure; vertical centring is the slide's align-content */
178
+ }
179
+
121
180
  .carousel__caption {
122
181
  color: inherit;
123
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getvitops/core",
3
- "version": "8.0.0",
3
+ "version": "8.0.1",
4
4
  "description": "The Vitops design-system framework — CSS partials, Lit web components, and browser polyfills.",
5
5
  "homepage": "https://docs.vitops.ca",
6
6
  "bugs": "https://github.com/getvitops/design-system/issues",
@@ -46,7 +46,7 @@
46
46
  "scroll-timeline-polyfill": "^1.1.0",
47
47
  "temporal-polyfill": "^1.0.1",
48
48
  "vitest": "4.1.9",
49
- "@getvitops/utils": "8.0.0"
49
+ "@getvitops/utils": "8.0.1"
50
50
  },
51
51
  "engines": {
52
52
  "node": ">=24"