@grantcodes/ui 2.7.0 → 2.8.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,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.8.1](https://github.com/grantcodes/ui/compare/ui-v2.8.0...ui-v2.8.1) (2026-04-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **gallery:** filmstrip images should fill height with auto width, not square crop ([62d436f](https://github.com/grantcodes/ui/commit/62d436fe75f42c6d99c380c9d4384e53a97abef1))
9
+ * **gallery:** SSR-safe filmstrip variant with variant prop refactor ([2af368c](https://github.com/grantcodes/ui/commit/2af368cba60b55021f563a06c1416219abb0b024))
10
+ * **gallery:** use CSS custom properties for filmstrip styles instead of JS attribute propagation ([f83efcf](https://github.com/grantcodes/ui/commit/f83efcfcf24a896dcdcb943b5988a9daefcf8947))
11
+
12
+ ## [2.8.0](https://github.com/grantcodes/ui/compare/ui-v2.7.0...ui-v2.8.0) (2026-04-07)
13
+
14
+
15
+ ### Features
16
+
17
+ * **09-gallery-film-strip:** add filmstrip and marquee gallery variants ([0a4ff3e](https://github.com/grantcodes/ui/commit/0a4ff3e620bc21a6d291943acc72608d1c08d371))
18
+ * **ui:** gallery filmstrip variant ([d22a93d](https://github.com/grantcodes/ui/commit/d22a93d615874ca76fcf7525ea8d1dfa71721e2b))
19
+
3
20
  ## [2.7.0](https://github.com/grantcodes/ui/compare/ui-v2.6.0...ui-v2.7.0) (2026-04-06)
4
21
 
5
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grantcodes/ui",
3
- "version": "2.7.0",
3
+ "version": "2.8.1",
4
4
  "description": "A personal component system built with Lit web components",
5
5
  "type": "module",
6
6
  "main": "src/main.js",
@@ -47,7 +47,7 @@
47
47
  "@lit/react": "^1.0.8",
48
48
  "lit": "^3.3.1",
49
49
  "shiki": "^3.17.1",
50
- "@grantcodes/style-dictionary": "^1.4.1"
50
+ "@grantcodes/style-dictionary": "^1.4.2"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12",
@@ -3,17 +3,19 @@ import { html } from "lit/static-html.js";
3
3
  import galleryStyles from "./gallery.css" with { type: "css" };
4
4
 
5
5
  export class GrantCodesGallery extends LitElement {
6
- // Styles are scoped to this element: they won't conflict with styles
7
- // on the main page or in other components. Styling API can be exposed
8
- // via CSS custom properties.
9
6
  static styles = [galleryStyles];
10
7
 
8
+ static properties = {
9
+ variant: { type: String, reflect: true },
10
+ };
11
+
11
12
  /** @type {any[]} */
12
13
  images = [];
13
14
 
14
- // Define reactive properties--updating a reactive property causes
15
- // the component to update.
16
- // @property() label = 'Button Label'
15
+ constructor() {
16
+ super();
17
+ this.variant = "default";
18
+ }
17
19
 
18
20
  render() {
19
21
  return html`
@@ -38,8 +38,6 @@
38
38
  display: block;
39
39
  width: 100%;
40
40
  height: auto;
41
- /* Make the image square */
42
- aspect-ratio: 1;
43
41
  object-fit: cover;
44
42
  object-position: center;
45
43
  }
@@ -55,3 +53,89 @@
55
53
  color: white;
56
54
  }
57
55
 
56
+ /* =============================================
57
+ GALLERY-IMAGE DEFAULT STYLES
58
+ (applied inside grantcodes-gallery-image shadow DOM via :host)
59
+ ============================================= */
60
+
61
+ /* Default square crop. :host selector comes after .gallery__image img so
62
+ wins on equal specificity. Uses custom properties so filmstrip mode can
63
+ override them via inheritance from the parent gallery element. */
64
+ :host .gallery__image img {
65
+ aspect-ratio: var(--_gallery-img-aspect-ratio, 1);
66
+ block-size: var(--_gallery-img-block-size, auto);
67
+ width: var(--_gallery-img-width, 100%);
68
+ height: var(--_gallery-img-height, auto);
69
+ max-inline-size: var(--_gallery-img-max-inline-size, unset);
70
+ }
71
+
72
+ :host .gallery__image {
73
+ block-size: var(--_gallery-image-block-size, auto);
74
+ width: var(--_gallery-image-width, 100%);
75
+ }
76
+
77
+ /* =============================================
78
+ FILMSTRIP VARIANT
79
+ ============================================= */
80
+
81
+ /* Set inherited CSS custom properties on the gallery host when in filmstrip
82
+ mode. These cascade down to slotted grantcodes-gallery-image children
83
+ without requiring JS attribute propagation, making the filmstrip work
84
+ correctly with SSR-rendered content. */
85
+ :host([variant="filmstrip"]) {
86
+ --_gallery-image-block-size: 100%;
87
+ --_gallery-image-width: fit-content;
88
+ --_gallery-img-aspect-ratio: auto;
89
+ --_gallery-img-block-size: 100%;
90
+ --_gallery-img-width: auto;
91
+ --_gallery-img-height: 100%;
92
+ --_gallery-img-max-inline-size: none;
93
+ }
94
+
95
+ :host([variant="filmstrip"]) .gallery {
96
+ overflow: hidden;
97
+ }
98
+
99
+ :host([variant="filmstrip"]) .gallery__slot {
100
+ display: flex;
101
+ flex-wrap: nowrap;
102
+ overflow-x: auto;
103
+ overflow-y: hidden;
104
+ block-size: var(--filmstrip-height, 240px);
105
+ gap: var(--gallery-gap, 4px);
106
+ scroll-snap-type: x mandatory;
107
+ overscroll-behavior-x: contain;
108
+ scrollbar-width: none;
109
+ }
110
+
111
+ :host([variant="filmstrip"]) .gallery__slot::-webkit-scrollbar {
112
+ display: none;
113
+ }
114
+
115
+ :host([variant="filmstrip"]) .gallery__slot::slotted(*) {
116
+ flex: 0 0 auto;
117
+ block-size: 100%;
118
+ scroll-snap-align: start;
119
+ }
120
+
121
+ /* Scroll-driven reveal animation */
122
+ @supports (animation-timeline: view()) {
123
+ @media (prefers-reduced-motion: no-preference) {
124
+ :host([variant="filmstrip"]) .gallery__slot::slotted(*) {
125
+ animation: filmstrip-reveal linear both;
126
+ animation-timeline: view(inline);
127
+ animation-range: entry 0% entry 60%;
128
+ }
129
+ }
130
+ }
131
+
132
+ @keyframes filmstrip-reveal {
133
+ from {
134
+ opacity: 0.3;
135
+ scale: 0.92;
136
+ }
137
+ to {
138
+ opacity: 1;
139
+ scale: 1;
140
+ }
141
+ }
@@ -58,3 +58,46 @@ const meta = {
58
58
  export default meta;
59
59
 
60
60
  export const Gallery = {};
61
+
62
+ const filmstripImages = [
63
+ { w: 400, h: 240 },
64
+ { w: 160, h: 240 },
65
+ { w: 360, h: 240 },
66
+ { w: 180, h: 240 },
67
+ { w: 160, h: 900 }, // tall portrait image
68
+ { w: 420, h: 240 },
69
+ { w: 160, h: 240 },
70
+ { w: 380, h: 240 },
71
+ { w: 200, h: 240 },
72
+ { w: 440, h: 240 },
73
+ { w: 160, h: 240 },
74
+ { w: 350, h: 240 },
75
+ { w: 170, h: 240 },
76
+ ];
77
+
78
+ export const Filmstrip = {
79
+ args: {
80
+ variant: "filmstrip",
81
+ },
82
+ render: (args) =>
83
+ template(
84
+ args,
85
+ html`${filmstripImages.map(
86
+ ({ w, h }, i) =>
87
+ html`<grantcodes-gallery-image
88
+ src="https://picsum.photos/seed/${i + 10}/${w}/${h}"
89
+ alt="Photo ${i + 1}"
90
+ width="${w}"
91
+ height="${h}"
92
+ ></grantcodes-gallery-image>`,
93
+ )}`,
94
+ ),
95
+ parameters: {
96
+ docs: {
97
+ description: {
98
+ story:
99
+ "Filmstrip variant: images in a scrollable horizontal row at uniform height. Uses scroll-snap and scroll-driven animations.",
100
+ },
101
+ },
102
+ },
103
+ };
@@ -2,6 +2,7 @@ import { describe, it, afterEach } from "node:test";
2
2
  import { strict as assert } from "node:assert";
3
3
  import { fixture, cleanup } from "../../test-utils/index.js";
4
4
  import "./gallery.js";
5
+ import "./gallery-image.js";
5
6
 
6
7
  describe("Gallery Component", () => {
7
8
  let element;
@@ -57,4 +58,28 @@ describe("Gallery Component", () => {
57
58
  "Images array should be empty initially",
58
59
  );
59
60
  });
61
+
62
+ describe("Gallery filmstrip variant", () => {
63
+ let element;
64
+
65
+ afterEach(() => {
66
+ cleanup(element);
67
+ });
68
+
69
+ it("should have variant property default to 'default'", async () => {
70
+ element = await fixture("grantcodes-gallery");
71
+ assert.strictEqual(element.variant, "default", "variant should default to 'default'");
72
+ });
73
+
74
+ it("should reflect variant attribute when property is set", async () => {
75
+ element = await fixture("grantcodes-gallery");
76
+ element.variant = "filmstrip";
77
+ await element.updateComplete;
78
+ assert.strictEqual(
79
+ element.getAttribute("variant"),
80
+ "filmstrip",
81
+ "variant attribute should be reflected",
82
+ );
83
+ });
84
+ });
60
85
  });