@grantcodes/ui 2.15.2 → 2.15.6

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,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.15.6](https://github.com/grantcodes/ui/compare/ui-v2.15.5...ui-v2.15.6) (2026-06-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **ui:** raise sticky app bar layer ([8675995](https://github.com/grantcodes/ui/commit/8675995bcc8eb07596fa741a9e86f1ca1f7b0986))
9
+
10
+ ## [2.15.5](https://github.com/grantcodes/ui/compare/ui-v2.15.4...ui-v2.15.5) (2026-06-07)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **ui:** handle app bar nav overflow ([592f8b0](https://github.com/grantcodes/ui/commit/592f8b0a29bd08a49b536dd8691ae9050cb009db))
16
+
17
+ ## [2.15.4](https://github.com/grantcodes/ui/compare/ui-v2.15.3...ui-v2.15.4) (2026-06-07)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **ui:** reflect app bar boolean attrs ([10f0685](https://github.com/grantcodes/ui/commit/10f06854563526201031734318a01bc042bfdb8a))
23
+
24
+ ## [2.15.3](https://github.com/grantcodes/ui/compare/ui-v2.15.2...ui-v2.15.3) (2026-06-07)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **ui:** h2 margins ([2dac517](https://github.com/grantcodes/ui/commit/2dac517a704817f55f2d46df2fe92340a8c5fb94))
30
+
3
31
  ## [2.15.2](https://github.com/grantcodes/ui/compare/ui-v2.15.1...ui-v2.15.2) (2026-06-07)
4
32
 
5
33
 
@@ -234,7 +234,8 @@
234
234
  },
235
235
  "description": "Whether the app bar is sticky",
236
236
  "default": "false",
237
- "attribute": "sticky"
237
+ "attribute": "sticky",
238
+ "reflects": true
238
239
  },
239
240
  {
240
241
  "kind": "field",
@@ -245,7 +246,8 @@
245
246
  },
246
247
  "description": "Whether the app bar has transparent background",
247
248
  "default": "false",
248
- "attribute": "transparent"
249
+ "attribute": "transparent",
250
+ "reflects": true
249
251
  }
250
252
  ],
251
253
  "events": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grantcodes/ui",
3
- "version": "2.15.2",
3
+ "version": "2.15.6",
4
4
  "description": "A personal component system built with Lit web components",
5
5
  "type": "module",
6
6
  "main": "src/main.js",
@@ -10,8 +10,8 @@ export class GrantCodesAppBar extends LitElement {
10
10
  static styles = [focusRingStyles, appBarStyles];
11
11
 
12
12
  static properties = {
13
- sticky: { type: Boolean },
14
- transparent: { type: Boolean },
13
+ sticky: { type: Boolean, reflect: true },
14
+ transparent: { type: Boolean, reflect: true },
15
15
  _mobileMenuOpen: { type: Boolean, state: true },
16
16
  };
17
17
 
@@ -14,7 +14,7 @@
14
14
  background-color: var(--g-color-background-default);
15
15
  border-block-end: 1px solid var(--g-color-border-default);
16
16
  position: relative;
17
- z-index: 100;
17
+ z-index: 1000;
18
18
  }
19
19
 
20
20
  :host([sticky]),
@@ -214,6 +214,35 @@
214
214
  align-items: center;
215
215
  gap: var(--g-spacing-xs, 0.25rem);
216
216
  flex: 1;
217
+ min-inline-size: 0;
218
+ overflow-x: auto;
219
+ flex-wrap: nowrap;
220
+ -webkit-overflow-scrolling: touch;
221
+ -webkit-mask-image: linear-gradient(
222
+ to right,
223
+ black calc(100% - 1.5rem),
224
+ transparent
225
+ );
226
+ mask-image: linear-gradient(
227
+ to right,
228
+ black calc(100% - 1.5rem),
229
+ transparent
230
+ );
231
+ scrollbar-width: thin;
232
+ scrollbar-color: var(--g-color-border-default) transparent;
233
+ }
234
+
235
+ .app-bar__nav::-webkit-scrollbar {
236
+ height: 4px;
237
+ }
238
+
239
+ .app-bar__nav::-webkit-scrollbar-track {
240
+ background: transparent;
241
+ }
242
+
243
+ .app-bar__nav::-webkit-scrollbar-thumb {
244
+ background: var(--g-color-border-default);
245
+ border-radius: 2px;
217
246
  }
218
247
 
219
248
  /* Reset mobile-open styles at desktop — nav is always inline */
@@ -225,10 +254,22 @@
225
254
  box-shadow: none;
226
255
  animation: none;
227
256
  flex-direction: row;
257
+ overflow-x: auto;
258
+ -webkit-mask-image: linear-gradient(
259
+ to right,
260
+ black calc(100% - 1.5rem),
261
+ transparent
262
+ );
263
+ mask-image: linear-gradient(
264
+ to right,
265
+ black calc(100% - 1.5rem),
266
+ transparent
267
+ );
228
268
  }
229
269
 
230
270
  .app-bar__actions {
231
271
  order: 3;
272
+ flex-shrink: 0;
232
273
  }
233
274
 
234
275
  .app-bar__menu-button {
@@ -239,6 +280,11 @@
239
280
  .app-bar__overlay--visible {
240
281
  display: none;
241
282
  }
283
+
284
+ ::slotted(ul),
285
+ ::slotted(div[slot="nav"]) {
286
+ flex-shrink: 0;
287
+ }
242
288
  }
243
289
 
244
290
  /* Slotted content styling */
@@ -100,6 +100,43 @@ export const Transparent = {
100
100
  `,
101
101
  };
102
102
 
103
+ /**
104
+ * Many nav items demonstrating horizontal scroll overflow on desktop.
105
+ * When the viewport is wide enough for desktop mode but too narrow for all
106
+ * items, the nav becomes horizontally scrollable with a subtle fade affordance
107
+ * at the right edge and styled thin scrollbars.
108
+ */
109
+ export const ManyNavItems = {
110
+ render: () => html`
111
+ <grantcodes-app-bar>
112
+ <a slot="logo" href="/">MyApp</a>
113
+ <div slot="nav">
114
+ <grantcodes-nav-link><a href="/">Home</a></grantcodes-nav-link>
115
+ <grantcodes-nav-link><a href="/about">About</a></grantcodes-nav-link>
116
+ <grantcodes-nav-link><a href="/features">Features</a></grantcodes-nav-link>
117
+ <grantcodes-nav-link><a href="/pricing">Pricing</a></grantcodes-nav-link>
118
+ <grantcodes-nav-link><a href="/docs">Docs</a></grantcodes-nav-link>
119
+ <grantcodes-nav-link><a href="/blog">Blog</a></grantcodes-nav-link>
120
+ <grantcodes-nav-link><a href="/changelog">Changelog</a></grantcodes-nav-link>
121
+ <grantcodes-nav-link><a href="/api">API</a></grantcodes-nav-link>
122
+ <grantcodes-nav-link><a href="/support">Support</a></grantcodes-nav-link>
123
+ <grantcodes-nav-link><a href="/status">Status</a></grantcodes-nav-link>
124
+ </div>
125
+ <div slot="actions">
126
+ <grantcodes-button>Sign In</grantcodes-button>
127
+ </div>
128
+ </grantcodes-app-bar>
129
+ `,
130
+ parameters: {
131
+ docs: {
132
+ description: {
133
+ story:
134
+ "Resize the viewport to see the nav items become horizontally scrollable on desktop with a right-edge fade affordance and thin scrollbar.",
135
+ },
136
+ },
137
+ },
138
+ };
139
+
103
140
  /**
104
141
  * App bar with multiple action buttons.
105
142
  */
@@ -55,6 +55,14 @@ describe("App Bar Component", () => {
55
55
  assert.ok(appBar, "Should have sticky class");
56
56
  });
57
57
 
58
+ it("should reflect sticky to the host attribute", async () => {
59
+ element = await fixture("grantcodes-app-bar", {
60
+ sticky: true,
61
+ });
62
+
63
+ assert.ok(element.hasAttribute("sticky"), "Should reflect sticky attribute");
64
+ });
65
+
58
66
  it("should apply transparent class when transparent", async () => {
59
67
  element = await fixture("grantcodes-app-bar", {
60
68
  transparent: true,
@@ -64,6 +72,17 @@ describe("App Bar Component", () => {
64
72
  assert.ok(appBar, "Should have transparent class");
65
73
  });
66
74
 
75
+ it("should reflect transparent to the host attribute", async () => {
76
+ element = await fixture("grantcodes-app-bar", {
77
+ transparent: true,
78
+ });
79
+
80
+ assert.ok(
81
+ element.hasAttribute("transparent"),
82
+ "Should reflect transparent attribute",
83
+ );
84
+ });
85
+
67
86
  it("should have logo slot", async () => {
68
87
  element = await fixture("grantcodes-app-bar");
69
88
  const logoSlot = element.shadowRoot.querySelector('slot[name="logo"]');
@@ -1,6 +1,7 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import ctaStyles from "./cta.css" with { type: "css" };
3
3
  import "../button/button.js";
4
+ import "../container/container.js";
4
5
 
5
6
  export class GrantCodesCta extends LitElement {
6
7
  static styles = [ctaStyles];
@@ -69,7 +70,7 @@ export class GrantCodesCta extends LitElement {
69
70
  const secondary = this._secondaryAction;
70
71
  return html`
71
72
  <section class="cta">
72
- <div class="cta__container">
73
+ <grantcodes-container align="prose">
73
74
  ${
74
75
  this.eyebrow
75
76
  ? html`<p class="cta__eyebrow">${this.eyebrow}</p>`
@@ -101,7 +102,7 @@ export class GrantCodesCta extends LitElement {
101
102
  `
102
103
  : null
103
104
  }
104
- </div>
105
+ </grantcodes-container>
105
106
  </section>
106
107
  `;
107
108
  }
@@ -10,15 +10,9 @@
10
10
 
11
11
  .cta {
12
12
  padding-block: var(--g-spacing-3xl);
13
- padding-inline: var(--g-spacing-md);
14
13
  background: var(--g-color-background-subtle);
15
14
  }
16
15
 
17
- .cta__container {
18
- max-width: 65ch;
19
- margin: 0 auto;
20
- }
21
-
22
16
  :host([align="center"]) .cta {
23
17
  text-align: center;
24
18
  }
@@ -3,6 +3,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js";
3
3
  import * as icons from "lucide-static";
4
4
  import featureListStyles from "./feature-list.css" with { type: "css" };
5
5
  import "../icon/icon.js";
6
+ import "../container/container.js";
6
7
 
7
8
  export class GrantCodesFeatureList extends LitElement {
8
9
  static styles = [featureListStyles];
@@ -67,7 +68,7 @@ export class GrantCodesFeatureList extends LitElement {
67
68
  const items = this._items;
68
69
  return html`
69
70
  <section class="feature-list">
70
- <div class="feature-list__container">
71
+ <grantcodes-container>
71
72
  ${
72
73
  this.title
73
74
  ? html`<h2 class="feature-list__title">${this.title}</h2>`
@@ -123,7 +124,7 @@ export class GrantCodesFeatureList extends LitElement {
123
124
  `,
124
125
  )}
125
126
  </ul>
126
- </div>
127
+ </grantcodes-container>
127
128
  </section>
128
129
  `;
129
130
  }
@@ -10,12 +10,6 @@
10
10
 
11
11
  .feature-list {
12
12
  padding-block: var(--g-spacing-3xl);
13
- padding-inline: var(--g-spacing-md);
14
- }
15
-
16
- .feature-list__container {
17
- max-width: 1200px;
18
- margin: 0 auto;
19
13
  }
20
14
 
21
15
  .feature-list__title {
@@ -1,6 +1,7 @@
1
1
  import { LitElement } from "lit";
2
2
  import { html } from "lit/static-html.js";
3
3
  import footerStyles from "./footer.css" with { type: "css" };
4
+ import "../container/container.js";
4
5
 
5
6
  export class GrantCodesFooter extends LitElement {
6
7
  static styles = [footerStyles];
@@ -22,7 +23,7 @@ export class GrantCodesFooter extends LitElement {
22
23
  render() {
23
24
  return html`
24
25
  <footer class="footer">
25
- <div class="footer__container">
26
+ <grantcodes-container align="wide">
26
27
  <div class="footer__columns" style="--footer-columns: ${this.columns}">
27
28
  <slot></slot>
28
29
  </div>
@@ -31,7 +32,7 @@ export class GrantCodesFooter extends LitElement {
31
32
  <div class="footer__bottom">
32
33
  <slot name="bottom"></slot>
33
34
  </div>
34
- </div>
35
+ </grantcodes-container>
35
36
  </footer>
36
37
  `;
37
38
  }
@@ -12,17 +12,11 @@
12
12
 
13
13
  .footer {
14
14
  margin-block-start: auto;
15
+ padding-block: var(--g-spacing-2xl) var(--g-spacing-xl);
15
16
  border-block-start: 1px solid var(--g-color-border-subtle);
16
17
  background-color: var(--g-color-background-subtle);
17
18
  }
18
19
 
19
- .footer__container {
20
- padding-block: var(--g-spacing-2xl) var(--g-spacing-xl);
21
- padding-inline: var(--g-spacing-md);
22
- max-inline-size: 1400px;
23
- margin-inline: auto;
24
- }
25
-
26
20
  .footer__columns {
27
21
  display: grid;
28
22
  grid-template-columns: repeat(var(--footer-columns, 3), 1fr);
@@ -86,7 +86,7 @@ describe("Footer Component", () => {
86
86
 
87
87
  it("should have footer container", async () => {
88
88
  element = await fixture("grantcodes-footer");
89
- const container = element.shadowRoot.querySelector(".footer__container");
89
+ const container = element.shadowRoot.querySelector("grantcodes-container");
90
90
  assert.ok(container, "Footer container should exist");
91
91
  });
92
92
 
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import logoCloudStyles from "./logo-cloud.css" with { type: "css" };
3
+ import "../container/container.js";
3
4
 
4
5
  export class GrantCodesLogoCloud extends LitElement {
5
6
  static styles = [logoCloudStyles];
@@ -35,7 +36,7 @@ export class GrantCodesLogoCloud extends LitElement {
35
36
  const logos = this._logos;
36
37
  return html`
37
38
  <section class="logo-cloud">
38
- <div class="logo-cloud__container">
39
+ <grantcodes-container>
39
40
  ${
40
41
  this.title
41
42
  ? html`<p class="logo-cloud__title">${this.title}</p>`
@@ -74,7 +75,7 @@ export class GrantCodesLogoCloud extends LitElement {
74
75
  `,
75
76
  )}
76
77
  </ul>
77
- </div>
78
+ </grantcodes-container>
78
79
  </section>
79
80
  `;
80
81
  }
@@ -10,16 +10,10 @@
10
10
 
11
11
  .logo-cloud {
12
12
  padding-block: var(--g-spacing-2xl);
13
- padding-inline: var(--g-spacing-md);
14
13
  border-top: 1px solid var(--g-color-border-default);
15
14
  border-bottom: 1px solid var(--g-color-border-default);
16
15
  }
17
16
 
18
- .logo-cloud__container {
19
- max-width: 1200px;
20
- margin: 0 auto;
21
- }
22
-
23
17
  .logo-cloud__title {
24
18
  margin: 0 0 var(--g-spacing-xl);
25
19
  font: var(--g-typography-body-sm-font);
@@ -1,6 +1,7 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import pricingStyles from "./pricing.css" with { type: "css" };
3
3
  import "../button/button.js";
4
+ import "../container/container.js";
4
5
 
5
6
  export class GrantCodesPricing extends LitElement {
6
7
  static styles = [pricingStyles];
@@ -43,7 +44,7 @@ export class GrantCodesPricing extends LitElement {
43
44
  const tiers = this._tiers;
44
45
  return html`
45
46
  <section class="pricing">
46
- <div class="pricing__container">
47
+ <grantcodes-container>
47
48
  ${
48
49
  this.title
49
50
  ? html`<h2 class="pricing__title">${this.title}</h2>`
@@ -112,7 +113,7 @@ export class GrantCodesPricing extends LitElement {
112
113
  `,
113
114
  )}
114
115
  </ul>
115
- </div>
116
+ </grantcodes-container>
116
117
  </section>
117
118
  `;
118
119
  }
@@ -10,12 +10,6 @@
10
10
 
11
11
  .pricing {
12
12
  padding-block: var(--g-spacing-3xl);
13
- padding-inline: var(--g-spacing-md);
14
- }
15
-
16
- .pricing__container {
17
- max-width: 1200px;
18
- margin: 0 auto;
19
13
  }
20
14
 
21
15
  .pricing__title {
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import statsStyles from "./stats.css" with { type: "css" };
3
+ import "../container/container.js";
3
4
 
4
5
  export class GrantCodesStats extends LitElement {
5
6
  static styles = [statsStyles];
@@ -41,7 +42,7 @@ export class GrantCodesStats extends LitElement {
41
42
  const items = this._items;
42
43
  return html`
43
44
  <section class="stats">
44
- <div class="stats__container">
45
+ <grantcodes-container>
45
46
  ${
46
47
  this.title
47
48
  ? html`<h2 class="stats__title">${this.title}</h2>`
@@ -66,7 +67,7 @@ export class GrantCodesStats extends LitElement {
66
67
  `,
67
68
  )}
68
69
  </ul>
69
- </div>
70
+ </grantcodes-container>
70
71
  </section>
71
72
  `;
72
73
  }
@@ -10,15 +10,9 @@
10
10
 
11
11
  .stats {
12
12
  padding-block: var(--g-spacing-3xl);
13
- padding-inline: var(--g-spacing-md);
14
13
  background: var(--g-color-background-subtle);
15
14
  }
16
15
 
17
- .stats__container {
18
- max-width: 1200px;
19
- margin: 0 auto;
20
- }
21
-
22
16
  .stats__title {
23
17
  margin: 0 0 var(--g-spacing-xl);
24
18
  font: var(--g-typography-h3-font);
@@ -2,6 +2,7 @@ import { LitElement, html } from "lit";
2
2
  import testimonialsStyles from "./testimonials.css" with { type: "css" };
3
3
  import "../card/card.js";
4
4
  import "../person/person.js";
5
+ import "../container/container.js";
5
6
 
6
7
  export class GrantCodesTestimonials extends LitElement {
7
8
  static styles = [testimonialsStyles];
@@ -43,7 +44,7 @@ export class GrantCodesTestimonials extends LitElement {
43
44
  const items = this._items;
44
45
  return html`
45
46
  <section class="testimonials">
46
- <div class="testimonials__container">
47
+ <grantcodes-container>
47
48
  ${
48
49
  this.title
49
50
  ? html`<h2 class="testimonials__title">${this.title}</h2>`
@@ -76,7 +77,7 @@ export class GrantCodesTestimonials extends LitElement {
76
77
  `,
77
78
  )}
78
79
  </ul>
79
- </div>
80
+ </grantcodes-container>
80
81
  </section>
81
82
  `;
82
83
  }
@@ -10,12 +10,6 @@
10
10
 
11
11
  .testimonials {
12
12
  padding-block: var(--g-spacing-3xl);
13
- padding-inline: var(--g-spacing-md);
14
- }
15
-
16
- .testimonials__container {
17
- max-width: 1200px;
18
- margin: 0 auto;
19
13
  }
20
14
 
21
15
  .testimonials__title {
@@ -11,15 +11,15 @@
11
11
  }
12
12
  }
13
13
 
14
- :where(h1, h1, h3, h4, h5, h6, p) {
14
+ :where(h1, h2, h3, h4, h5, h6, p) {
15
15
  margin-block-end: 1rem;
16
16
  }
17
17
 
18
- :where(h1, h1, h3, h4, h5, h6, p):first-child {
18
+ :where(h1, h2, h3, h4, h5, h6, p):first-child {
19
19
  margin-block-start: 0;
20
20
  }
21
21
 
22
- :where(h1, h1, h3, h4, h5, h6, p):last-child {
22
+ :where(h1, h2, h3, h4, h5, h6, p):last-child {
23
23
  margin-block-end: 0;
24
24
  }
25
25