@grantcodes/ui 2.0.0 → 2.1.0

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.
Files changed (181) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/custom-elements.json +1926 -191
  3. package/package.json +7 -6
  4. package/src/components/accordion/accordion.component.js +33 -0
  5. package/src/components/accordion/accordion.js +6 -0
  6. package/src/components/accordion/accordion.stories.js +88 -0
  7. package/src/components/accordion/accordion.styles.js +66 -0
  8. package/src/components/accordion/index.js +6 -0
  9. package/src/components/app-bar/app-bar.component.js +1 -3
  10. package/src/components/app-bar/app-bar.js +0 -2
  11. package/src/components/app-bar/app-bar.styles.js +222 -221
  12. package/src/components/app-bar/app-bar.test.js +58 -17
  13. package/src/components/app-bar/index.js +0 -2
  14. package/src/components/avatar/avatar.js +0 -12
  15. package/src/components/avatar/avatar.stories.js +0 -12
  16. package/src/components/avatar/avatar.styles.js +19 -19
  17. package/src/components/avatar/avatar.test.js +4 -4
  18. package/src/components/avatar/index.js +1 -13
  19. package/src/components/badge/badge.js +0 -2
  20. package/src/components/badge/badge.styles.js +78 -81
  21. package/src/components/badge/badge.test.js +18 -5
  22. package/src/components/badge/index.js +0 -2
  23. package/src/components/breadcrumb/breadcrumb.component.js +9 -10
  24. package/src/components/breadcrumb/breadcrumb.js +6 -4
  25. package/src/components/breadcrumb/breadcrumb.styles.js +86 -90
  26. package/src/components/breadcrumb/breadcrumb.test.js +15 -5
  27. package/src/components/breadcrumb/index.js +0 -2
  28. package/src/components/button/button.component.js +2 -2
  29. package/src/components/button/button.styles.js +58 -86
  30. package/src/components/button/button.test.js +8 -4
  31. package/src/components/button/index.js +1 -1
  32. package/src/components/button-group/button-group.test.js +0 -2
  33. package/src/components/button-group/index.js +1 -1
  34. package/src/components/card/card.component.js +40 -9
  35. package/src/components/card/card.js +3 -1
  36. package/src/components/card/card.stories.js +18 -5
  37. package/src/components/card/card.styles.js +46 -20
  38. package/src/components/card/card.test.js +0 -2
  39. package/src/components/card/index.js +1 -1
  40. package/src/components/code-preview/code-preview.component.js +9 -9
  41. package/src/components/code-preview/code-preview.js +0 -1
  42. package/src/components/code-preview/code-preview.styles.js +3 -3
  43. package/src/components/code-preview/code-preview.test.js +29 -8
  44. package/src/components/code-preview/index.js +1 -1
  45. package/src/components/container/container.component.js +1 -0
  46. package/src/components/container/container.js +0 -1
  47. package/src/components/container/container.stories.js +12 -4
  48. package/src/components/container/container.styles.js +37 -35
  49. package/src/components/container/container.test.js +0 -2
  50. package/src/components/container/index.js +1 -1
  51. package/src/components/cta/cta.component.js +108 -0
  52. package/src/components/cta/cta.js +6 -0
  53. package/src/components/cta/cta.stories.js +56 -0
  54. package/src/components/cta/cta.styles.js +64 -0
  55. package/src/components/cta/index.js +1 -0
  56. package/src/components/dialog/dialog.js +0 -1
  57. package/src/components/dialog/dialog.styles.js +8 -8
  58. package/src/components/dialog/dialog.test.js +11 -5
  59. package/src/components/dialog/index.js +1 -1
  60. package/src/components/dropdown/dropdown.component.js +5 -3
  61. package/src/components/dropdown/dropdown.js +6 -4
  62. package/src/components/dropdown/dropdown.styles.js +5 -5
  63. package/src/components/dropdown/dropdown.test.js +20 -4
  64. package/src/components/dropdown/index.js +0 -2
  65. package/src/components/dropzone/dropzone.component.js +7 -6
  66. package/src/components/dropzone/dropzone.styles.js +4 -4
  67. package/src/components/dropzone/dropzone.test.js +6 -4
  68. package/src/components/dropzone/index.js +1 -1
  69. package/src/components/feature-list/feature-list.component.js +130 -0
  70. package/src/components/feature-list/feature-list.js +6 -0
  71. package/src/components/feature-list/feature-list.stories.js +117 -0
  72. package/src/components/feature-list/feature-list.styles.js +82 -0
  73. package/src/components/feature-list/index.js +1 -0
  74. package/src/components/footer/footer-column.styles.js +46 -47
  75. package/src/components/footer/footer.js +6 -2
  76. package/src/components/footer/footer.styles.js +6 -6
  77. package/src/components/footer/footer.test.js +9 -4
  78. package/src/components/footer/index.js +1 -1
  79. package/src/components/form-field/form-field.component.js +1 -3
  80. package/src/components/form-field/form-field.js +0 -1
  81. package/src/components/form-field/form-field.styles.js +35 -37
  82. package/src/components/form-field/form-field.test.js +9 -4
  83. package/src/components/form-field/index.js +1 -1
  84. package/src/components/gallery/gallery-image.js +0 -1
  85. package/src/components/gallery/gallery.js +0 -1
  86. package/src/components/gallery/gallery.styles.js +1 -1
  87. package/src/components/gallery/gallery.test.js +5 -3
  88. package/src/components/gallery/index.js +2 -2
  89. package/src/components/hero/hero.component.js +66 -0
  90. package/src/components/hero/hero.js +6 -0
  91. package/src/components/hero/hero.stories.js +53 -0
  92. package/src/components/hero/hero.styles.js +46 -0
  93. package/src/components/hero/index.js +1 -0
  94. package/src/components/icon/icon.js +3 -2
  95. package/src/components/icon/icon.stories.js +2 -1
  96. package/src/components/icon/icon.styles.js +23 -21
  97. package/src/components/icon/icon.test.js +2 -3
  98. package/src/components/icon/index.js +1 -1
  99. package/src/components/loading/index.js +1 -1
  100. package/src/components/loading/loading.js +3 -2
  101. package/src/components/loading/loading.styles.js +1 -1
  102. package/src/components/loading/loading.test.js +0 -2
  103. package/src/components/logo-cloud/index.js +1 -0
  104. package/src/components/logo-cloud/logo-cloud.component.js +81 -0
  105. package/src/components/logo-cloud/logo-cloud.js +6 -0
  106. package/src/components/logo-cloud/logo-cloud.stories.js +107 -0
  107. package/src/components/logo-cloud/logo-cloud.styles.js +68 -0
  108. package/src/components/media-text/index.js +1 -0
  109. package/src/components/media-text/media-text.component.js +100 -0
  110. package/src/components/media-text/media-text.js +6 -0
  111. package/src/components/media-text/media-text.stories.js +69 -0
  112. package/src/components/media-text/media-text.styles.js +66 -0
  113. package/src/components/newsletter/index.js +1 -0
  114. package/src/components/newsletter/newsletter.component.js +101 -0
  115. package/src/components/newsletter/newsletter.js +6 -0
  116. package/src/components/newsletter/newsletter.stories.js +59 -0
  117. package/src/components/newsletter/newsletter.styles.js +89 -0
  118. package/src/components/notice/index.js +1 -1
  119. package/src/components/notice/notice.js +0 -1
  120. package/src/components/notice/notice.styles.js +7 -7
  121. package/src/components/notice/notice.test.js +15 -5
  122. package/src/components/pagination/index.js +1 -1
  123. package/src/components/pagination/pagination.stories.js +1 -3
  124. package/src/components/pagination/pagination.styles.js +1 -1
  125. package/src/components/pagination/pagination.test.js +9 -4
  126. package/src/components/pricing/index.js +1 -0
  127. package/src/components/pricing/pricing.component.js +119 -0
  128. package/src/components/pricing/pricing.js +6 -0
  129. package/src/components/pricing/pricing.stories.js +123 -0
  130. package/src/components/pricing/pricing.styles.js +135 -0
  131. package/src/components/sidebar/index.js +0 -2
  132. package/src/components/sidebar/sidebar.component.js +12 -10
  133. package/src/components/sidebar/sidebar.js +3 -3
  134. package/src/components/sidebar/sidebar.stories.js +0 -2
  135. package/src/components/sidebar/sidebar.styles.js +181 -186
  136. package/src/components/sidebar/sidebar.test.js +48 -13
  137. package/src/components/stats/index.js +1 -0
  138. package/src/components/stats/stats.component.js +73 -0
  139. package/src/components/stats/stats.js +6 -0
  140. package/src/components/stats/stats.stories.js +64 -0
  141. package/src/components/stats/stats.styles.js +66 -0
  142. package/src/components/tabs/index.js +2 -2
  143. package/src/components/tabs/internal/tabs-button.component.js +1 -1
  144. package/src/components/tabs/internal/tabs-button.js +0 -1
  145. package/src/components/tabs/tab.js +0 -1
  146. package/src/components/tabs/tabs.js +3 -2
  147. package/src/components/tabs/tabs.styles.js +84 -74
  148. package/src/components/testimonials/index.js +1 -0
  149. package/src/components/testimonials/testimonials.component.js +97 -0
  150. package/src/components/testimonials/testimonials.js +6 -0
  151. package/src/components/testimonials/testimonials.stories.js +78 -0
  152. package/src/components/testimonials/testimonials.styles.js +82 -0
  153. package/src/components/toast/index.js +0 -2
  154. package/src/components/toast/toast.component.js +1 -3
  155. package/src/components/toast/toast.js +10 -5
  156. package/src/components/toast/toast.stories.js +9 -5
  157. package/src/components/toast/toast.styles.js +199 -201
  158. package/src/components/toast/toast.test.js +38 -10
  159. package/src/components/tooltip/index.js +1 -1
  160. package/src/components/tooltip/tooltip.js +3 -2
  161. package/src/components/tooltip/tooltip.styles.js +3 -3
  162. package/src/components/tooltip/tooltip.test.js +10 -4
  163. package/src/css/base.css +8 -5
  164. package/src/css/colors.stories.js +27 -28
  165. package/src/css/elements/forms/input.css +9 -41
  166. package/src/css/elements/media/image.css +1 -1
  167. package/src/css/themes/grantcodes.css +3 -3
  168. package/src/css/themes/todomap.css +3 -2
  169. package/src/css/themes/wireframe.css +2 -2
  170. package/src/css/tokens.stories.js +26 -21
  171. package/src/css/typography.css +1 -3
  172. package/src/css/util/focus-ring.css +30 -0
  173. package/src/css/util/index.css +1 -2
  174. package/src/lib/styles/focus-ring.styles.js +34 -0
  175. package/src/main.js +10 -1
  176. package/src/pages/agency.stories.js +164 -0
  177. package/src/pages/blog-post.stories.js +381 -0
  178. package/src/pages/saas-landing.stories.js +307 -0
  179. package/src/test-utils/assert-helpers.js +10 -8
  180. package/src/css/util/functions.css +0 -16
  181. package/src/css/util/mixins.css +0 -63
@@ -1,97 +1,69 @@
1
1
  import { css } from "lit";
2
+ import { focusRingStyles } from "../../lib/styles/focus-ring.styles.js";
2
3
 
3
4
  export const buttonStyles = css`
4
- /* Inlined component-base mixin */
5
- *,
6
- *::before,
7
- *::after {
8
- box-sizing: border-box;
9
- }
5
+ ${focusRingStyles}
10
6
 
11
- :host {
12
- display: block;
13
- --button-border-radius-start: var(--g-theme-border-radius-md);
14
- --button-border-radius-end: var(--g-theme-border-radius-md);
15
- }
7
+ /* Inlined component-base mixin */
8
+ *,
9
+ *::before,
10
+ *::after {
11
+ box-sizing: border-box;
12
+ }
16
13
 
17
- .button {
18
- display: inline-flex;
19
- flex-direction: row;
20
- align-items: center;
21
- gap: 0.5rem;
22
- background: var(--g-theme-button-primary-color-background-default);
23
- color: var(--g-theme-button-primary-color-content-default);
24
- border-width: var(--g-theme-border-width-md);
25
- border-style: solid;
26
- border-color: var(--g-theme-button-primary-color-border-default);
27
- padding: 0.5em 1em;
28
- border-start-start-radius: var(--button-border-radius-start);
29
- border-start-end-radius: var(--button-border-radius-end);
30
- border-end-start-radius: var(--button-border-radius-start);
31
- border-end-end-radius: var(--button-border-radius-end);
32
- font-size: 1rem;
33
- font-weight: bold;
34
- letter-spacing: 0.05em;
35
- text-decoration: none;
36
- outline: 1px solid transparent;
37
- transition:
38
- color 0.2s,
39
- border-color 0.2s,
40
- background-color 0.2s,
41
- outline 0.2s;
14
+ :host {
15
+ display: block;
16
+ --button-border-radius-start: var(--g-theme-border-radius-md);
17
+ --button-border-radius-end: var(--g-theme-border-radius-md);
18
+ }
42
19
 
43
- /* Inlined focus-ring styles */
44
- outline-color: transparent;
45
- outline-offset: var(--g-theme-focus-ring-offset-default);
46
- outline-style: solid;
47
- outline-width: var(--g-theme-focus-ring-width-default);
48
- }
20
+ .button {
21
+ display: inline-flex;
22
+ flex-direction: row;
23
+ align-items: center;
24
+ gap: var(--g-theme-spacing-sm);
25
+ background: var(--g-theme-button-primary-color-background-default);
26
+ color: var(--g-theme-button-primary-color-content-default);
27
+ border-width: var(--g-theme-border-width-md);
28
+ border-style: solid;
29
+ border-color: var(--g-theme-button-primary-color-border-default);
30
+ padding-block: var(--g-theme-spacing-sm);
31
+ padding-inline: var(--g-theme-spacing-md);
32
+ border-start-start-radius: var(--button-border-radius-start);
33
+ border-start-end-radius: var(--button-border-radius-end);
34
+ border-end-start-radius: var(--button-border-radius-start);
35
+ border-end-end-radius: var(--button-border-radius-end);
36
+ font: var(--g-theme-typography-label-default);
37
+ font-family: inherit;
38
+ text-decoration: none;
39
+ transition-property: color, border-color, background-color, outline;
40
+ }
49
41
 
50
- .button:focus-visible {
51
- outline-color: var(--g-theme-focus-ring-color-default);
52
- }
42
+ .button:hover,
43
+ .button:focus-visible {
44
+ color: var(--g-theme-button-primary-color-content-hover);
45
+ background: var(--g-theme-button-primary-color-background-hover);
46
+ border-color: var(--g-theme-button-primary-color-border-hover);
47
+ cursor: pointer;
48
+ text-decoration: none;
49
+ }
53
50
 
54
- @media (prefers-contrast: more) {
55
- .button:focus-visible {
56
- outline-color: var(--g-theme-focus-ring-color-contrast);
57
- box-shadow: 0 0 0
58
- calc(
59
- (
60
- var(--g-theme-focus-ring-width-default) +
61
- var(--g-theme-focus-ring-offset-default)
62
- ) *
63
- 1.5
64
- )
65
- var(--g-theme-focus-ring-color-contrast-shadow);
66
- }
67
- }
51
+ .button:active {
52
+ color: var(--g-theme-button-primary-color-content-active);
53
+ background: var(--g-theme-button-primary-color-background-active);
54
+ border-color: var(--g-theme-button-primary-color-border-active);
55
+ }
68
56
 
69
- .button:hover,
70
- .button:focus-visible {
71
- color: var(--g-theme-button-primary-color-content-hover);
72
- background: var(--g-theme-button-primary-color-background-hover);
73
- border-color: var(--g-theme-button-primary-color-border-hover);
74
- cursor: pointer;
75
- text-decoration: none;
76
- }
77
-
78
- .button:active {
79
- color: var(--g-theme-button-primary-color-content-active);
80
- background: var(--g-theme-button-primary-color-background-active);
81
- border-color: var(--g-theme-button-primary-color-border-active);
82
- }
83
-
84
- .button:active {
85
- transform: translateY(1px);
86
- }
87
-
88
- .button[disabled] {
89
- background: var(--g-theme-button-primary-color-background-disabled);
90
- color: var(--g-theme-button-primary-color-content-disabled);
91
- border-color: var(--g-theme-button-primary-color-border-disabled);
92
- cursor: not-allowed;
93
- filter: grayscale(70%);
94
- opacity: 0.8;
95
- }
57
+ .button:active {
58
+ transform: translateY(1px);
59
+ }
96
60
 
61
+ .button[disabled] {
62
+ background: var(--g-theme-button-primary-color-background-disabled);
63
+ color: var(--g-theme-button-primary-color-content-disabled);
64
+ border-color: var(--g-theme-button-primary-color-border-disabled);
65
+ cursor: not-allowed;
66
+ filter: grayscale(70%);
67
+ opacity: 0.8;
68
+ }
97
69
  `;
@@ -32,7 +32,10 @@ describe("Button Component", () => {
32
32
  assert.ok(link, "Link element should exist");
33
33
  const href = link.getAttribute("href");
34
34
  assert.ok(href, "Link should have href attribute");
35
- assert.ok(href.includes("example.com") || href === "https://example.com", "Link href should contain example.com");
35
+ assert.ok(
36
+ href.includes("example.com") || href === "https://example.com",
37
+ "Link href should contain example.com",
38
+ );
36
39
  });
37
40
 
38
41
  it("should be disabled when disabled property is set", async () => {
@@ -42,7 +45,10 @@ describe("Button Component", () => {
42
45
 
43
46
  const button = element.shadowRoot.querySelector("button");
44
47
  assert.ok(button.disabled, "Button should be disabled");
45
- assert.ok(button.hasAttribute("disabled"), "Button should have disabled attribute");
48
+ assert.ok(
49
+ button.hasAttribute("disabled"),
50
+ "Button should have disabled attribute",
51
+ );
46
52
  });
47
53
 
48
54
  it("should emit click event when clicked", async () => {
@@ -94,5 +100,3 @@ describe("Button Component", () => {
94
100
  assert.ok(slot, "Slot should exist");
95
101
  });
96
102
  });
97
-
98
-
@@ -1 +1 @@
1
- export * from "./button";
1
+ export * from "./button.js";
@@ -53,5 +53,3 @@ describe("Button Group Component", () => {
53
53
  assert.ok(group, "Button group should provide visual grouping");
54
54
  });
55
55
  });
56
-
57
-
@@ -1 +1 @@
1
- export * from "./button-group";
1
+ export * from "./button-group.js";
@@ -1,17 +1,48 @@
1
- import { LitElement } from "lit";
2
- import { html } from "lit/static-html.js";
1
+ import { LitElement, html } from "lit";
3
2
  import { cardStyles } from "./card.styles.js";
4
3
 
5
4
  export class GrantCodesCard extends LitElement {
5
+ static properties = {
6
+ hasHeader: { type: Boolean, reflect: true },
7
+ hasFooter: { type: Boolean, reflect: true },
8
+ };
9
+
6
10
  static styles = [cardStyles];
7
11
 
12
+ constructor() {
13
+ super();
14
+ this.hasHeader = false;
15
+ this.hasFooter = false;
16
+ }
17
+
18
+ firstUpdated() {
19
+ this._checkSlots();
20
+ this.shadowRoot.querySelectorAll("slot").forEach((slot) => {
21
+ slot.addEventListener("slotchange", () => this._checkSlots());
22
+ });
23
+ }
24
+
25
+ _checkSlots() {
26
+ const headerSlot = this.shadowRoot?.querySelector('slot[name="header"]');
27
+ const footerSlot = this.shadowRoot?.querySelector('slot[name="footer"]');
28
+ this.hasHeader =
29
+ (headerSlot?.assignedNodes({ flatten: true }).length ?? 0) > 0;
30
+ this.hasFooter =
31
+ (footerSlot?.assignedNodes({ flatten: true }).length ?? 0) > 0;
32
+
33
+ const headerEl = this.shadowRoot?.querySelector(".card__header");
34
+ const footerEl = this.shadowRoot?.querySelector(".card__footer");
35
+ headerEl?.toggleAttribute("data-has-content", this.hasHeader);
36
+ footerEl?.toggleAttribute("data-has-content", this.hasFooter);
37
+ }
38
+
8
39
  render() {
9
- return html`
10
- <div class="card">
11
- <div class="card__header"><slot name="header"></slot></div>
12
- <div class="card__content"><slot></slot></div>
13
- <div class="card__footer"><slot name="footer"></slot></div>
14
- </div>
15
- `;
40
+ return html`
41
+ <div class="card">
42
+ <div class="card__header"><slot name="header"></slot></div>
43
+ <div class="card__content"><slot></slot></div>
44
+ <div class="card__footer"><slot name="footer"></slot></div>
45
+ </div>
46
+ `;
16
47
  }
17
48
  }
@@ -3,4 +3,6 @@ import { GrantCodesCard } from "./card.component.js";
3
3
  export * from "./card.component.js";
4
4
  export default GrantCodesCard;
5
5
 
6
- customElements.define("grantcodes-card", GrantCodesCard);
6
+ if (!customElements.get("grantcodes-card")) {
7
+ customElements.define("grantcodes-card", GrantCodesCard);
8
+ }
@@ -18,11 +18,11 @@ const meta = {
18
18
  template(
19
19
  args,
20
20
  html`<h3 slot="header">Card Header</h3>
21
- <p>${args.content}</p>
22
- <grantcodes-button-group slot="footer">
23
- <grantcodes-button>Action 1</grantcodes-button>
24
- <grantcodes-button>Action 2</grantcodes-button>
25
- </grantcodes-button-group>`,
21
+ <p>${args.content}</p>
22
+ <grantcodes-button-group slot="footer">
23
+ <grantcodes-button>Action 1</grantcodes-button>
24
+ <grantcodes-button>Action 2</grantcodes-button>
25
+ </grantcodes-button-group>`,
26
26
  ),
27
27
  parameters: {
28
28
  actions: {
@@ -34,3 +34,16 @@ const meta = {
34
34
  export default meta;
35
35
 
36
36
  export const Card = {};
37
+
38
+ export const CardInLink = {
39
+ render: () =>
40
+ html`<a
41
+ href="#"
42
+ style="text-decoration: none; color: inherit; display: block;"
43
+ >
44
+ <grantcodes-card class="is-clickable">
45
+ <h3 slot="header">Card in Link</h3>
46
+ <p>This card is wrapped in an anchor tag and has a hover effect.</p>
47
+ </grantcodes-card>
48
+ </a>`,
49
+ };
@@ -10,6 +10,7 @@ export const cardStyles = css`
10
10
 
11
11
  :host {
12
12
  display: block;
13
+ flex-grow: 1;
13
14
  /* Enable container queries for responsive card layouts */
14
15
  container-type: inline-size;
15
16
  container-name: card;
@@ -18,6 +19,7 @@ export const cardStyles = css`
18
19
  .card {
19
20
  display: flex;
20
21
  inline-size: 100%;
22
+ min-block-size: 100%;
21
23
  flex-direction: column;
22
24
  justify-content: flex-start;
23
25
  overflow: hidden;
@@ -26,47 +28,49 @@ export const cardStyles = css`
26
28
  border-width: var(--g-theme-border-width-sm);
27
29
  border-color: var(--g-theme-color-border-default);
28
30
  border-radius: var(--g-theme-border-radius-md);
29
- box-shadow:
30
- 0 1px 3px rgba(0, 0, 0, 0.12),
31
- 0 1px 2px rgba(0, 0, 0, 0.24);
31
+ box-shadow: var(--g-theme-box-shadow-md);
32
32
  transition:
33
33
  box-shadow 0.2s ease,
34
34
  transform 0.2s ease,
35
35
  border-color 0.2s ease,
36
36
  background-color 0.2s ease;
37
- box-shadow:
38
- 0 8px 24px rgba(0, 0, 0, 0.08),
39
- 0 6px 12px rgba(0, 0, 0, 0.04);
40
37
  }
41
38
 
42
39
  /* Image support - images in header or content will be styled appropriately */
43
40
  .card__header img,
44
41
  .card__content img {
45
42
  display: block;
46
- width: 100%;
47
- height: auto;
43
+ inline-size: 100%;
44
+ block-size: auto;
48
45
  border-radius: inherit;
49
46
  }
50
47
 
51
48
  .card__header {
52
- padding-inline: 1rem;
53
- padding-block: calc(1rem * 0.75);
49
+ padding-inline: var(--g-theme-spacing-md);
50
+ padding-block: var(--g-spacing-12);
54
51
  border-block-end: 1px solid var(--g-theme-color-border-default);
55
52
  }
56
53
 
54
+ .card__header:has(> slot:empty) {
55
+ display: none;
56
+ }
57
+
57
58
  .card__header ::slotted(*) {
58
59
  margin: 0;
59
- font-weight: 600;
60
- font-size: var(--g-typography-font-size-24);
60
+ font: var(--g-theme-typography-title-default);
61
61
  }
62
62
 
63
63
  .card__content {
64
64
  display: flex;
65
65
  flex-direction: column;
66
66
  justify-content: flex-start;
67
- gap: 1rem;
68
- padding-block: 1rem;
69
- padding-inline: 1rem;
67
+ gap: var(--g-theme-spacing-md);
68
+ padding-block: var(--g-theme-spacing-md);
69
+ padding-inline: var(--g-theme-spacing-md);
70
+ }
71
+
72
+ .card__content:has(::slotted(:empty)) {
73
+ display: none;
70
74
  }
71
75
 
72
76
  .card__content > * {
@@ -77,12 +81,19 @@ export const cardStyles = css`
77
81
  display: flex;
78
82
  flex-direction: row;
79
83
  flex-wrap: wrap;
80
- justify-content: flex-end;
81
- gap: calc(1rem * 0.5);
84
+ justify-content: flex-start;
85
+ align-items: center;
86
+ gap: var(--g-theme-spacing-sm);
87
+ padding-inline: var(--g-theme-spacing-md);
88
+ padding-block: var(--g-spacing-12);
82
89
  border-block-start: 1px solid var(--g-theme-color-border-default);
83
90
  background: var(--g-theme-color-background-default);
84
91
  }
85
92
 
93
+ .card__footer:has(> slot:empty) {
94
+ display: none;
95
+ }
96
+
86
97
  .card__footer ::slotted(grantcodes-button-group) {
87
98
  width: 100%;
88
99
  justify-content: flex-end;
@@ -93,11 +104,11 @@ export const cardStyles = css`
93
104
  flex-direction: row;
94
105
  align-items: center;
95
106
  flex-wrap: wrap;
96
- gap: 1rem;
107
+ gap: var(--g-theme-spacing-md);
97
108
  margin: 0;
98
- padding-inline: 1rem;
109
+ padding-inline: var(--g-theme-spacing-md);
99
110
  padding-block: 1em;
100
- font-size: var(--g-typography-font-size-14);
111
+ font-size: var(--g-theme-typography-meta-sm-font-size);
101
112
  /* Use color-mix() for modern color manipulation */
102
113
  background-color: color-mix(
103
114
  in srgb,
@@ -125,4 +136,19 @@ export const cardStyles = css`
125
136
  .card__meta__item dt::after {
126
137
  content: ":";
127
138
  }
139
+
140
+ :host(.is-clickable) .card {
141
+ cursor: pointer;
142
+ }
143
+
144
+ :host(.is-clickable:hover) .card {
145
+ transform: translateY(-2px);
146
+ box-shadow: var(--g-theme-box-shadow-lg);
147
+ border-color: var(--g-theme-color-border-default);
148
+ }
149
+
150
+ :host(.is-clickable:active) .card {
151
+ transform: translateY(-1px);
152
+ border-color: var(--g-theme-color-border-active);
153
+ }
128
154
  `;
@@ -55,5 +55,3 @@ describe("Card Component", () => {
55
55
  );
56
56
  });
57
57
  });
58
-
59
-
@@ -1 +1 @@
1
- export * from "./card";
1
+ export * from "./card.js";
@@ -18,10 +18,10 @@ export class GrantCodesCodePreview extends LitElement {
18
18
  this.codePreview = null;
19
19
  }
20
20
 
21
- async doHighlight() {
21
+ async doHighlight() {
22
22
  const rawCode = this.textContent ?? "";
23
- const { codeToHtml } = await import("shiki/bundle/web");
24
- const highlightedCode = await codeToHtml(rawCode.trim(), {
23
+ const { codeToHtml } = await import("shiki/bundle/web");
24
+ const highlightedCode = await codeToHtml(rawCode.trim(), {
25
25
  lang: this.language,
26
26
  theme: this.theme,
27
27
  });
@@ -29,13 +29,13 @@ export class GrantCodesCodePreview extends LitElement {
29
29
  this.codePreview.innerHTML = highlightedCode;
30
30
  }
31
31
 
32
- async firstUpdated() {
32
+ async firstUpdated() {
33
33
  this.codePreview = this.renderRoot.querySelector(".code-preview");
34
- // Only highlight if we have content and we're in a browser environment,
35
- // and not in a unit test environment
36
- const isTestEnv =
37
- typeof process !== "undefined" && process?.env?.NODE_ENV === "test";
38
- if (this.textContent && typeof window !== "undefined" && !isTestEnv) {
34
+ // Only highlight if we have content and we're in a browser environment,
35
+ // and not in a unit test environment
36
+ const isTestEnv =
37
+ typeof process !== "undefined" && process?.env?.NODE_ENV === "test";
38
+ if (this.textContent && typeof window !== "undefined" && !isTestEnv) {
39
39
  try {
40
40
  await this.doHighlight();
41
41
  } catch (error) {
@@ -4,4 +4,3 @@ export * from "./code-preview.component.js";
4
4
  export default GrantCodesCodePreview;
5
5
 
6
6
  customElements.define("grantcodes-code-preview", GrantCodesCodePreview);
7
-
@@ -11,8 +11,8 @@ export const codePreviewStyles = css`
11
11
  border-radius: var(--g-theme-border-radius-md);
12
12
  box-shadow: var(--g-theme-box-shadow-sm);
13
13
  font-size: var(--g-typography-font-size-14);
14
- font-weight: 400;
15
- padding: 1rem;
14
+ font-weight: var(--g-typography-font-weight-400);
15
+ padding: var(--g-theme-spacing-md);
16
16
  }
17
17
 
18
- `;
18
+ `;
@@ -26,12 +26,20 @@ describe.skip("Code Preview Component", () => {
26
26
 
27
27
  it("should have language property defaulting to html", async () => {
28
28
  element = await fixture("grantcodes-code-preview");
29
- assert.strictEqual(element.language, "html", "Language should default to html");
29
+ assert.strictEqual(
30
+ element.language,
31
+ "html",
32
+ "Language should default to html",
33
+ );
30
34
  });
31
35
 
32
36
  it("should have theme property defaulting to aurora-x", async () => {
33
37
  element = await fixture("grantcodes-code-preview");
34
- assert.strictEqual(element.theme, "aurora-x", "Theme should default to aurora-x");
38
+ assert.strictEqual(
39
+ element.theme,
40
+ "aurora-x",
41
+ "Theme should default to aurora-x",
42
+ );
35
43
  });
36
44
 
37
45
  it("should support different languages", async () => {
@@ -39,7 +47,11 @@ describe.skip("Code Preview Component", () => {
39
47
  language: "javascript",
40
48
  });
41
49
 
42
- assert.strictEqual(element.language, "javascript", "Language should be javascript");
50
+ assert.strictEqual(
51
+ element.language,
52
+ "javascript",
53
+ "Language should be javascript",
54
+ );
43
55
  });
44
56
 
45
57
  it("should support different themes", async () => {
@@ -47,7 +59,11 @@ describe.skip("Code Preview Component", () => {
47
59
  theme: "github-dark",
48
60
  });
49
61
 
50
- assert.strictEqual(element.theme, "github-dark", "Theme should be github-dark");
62
+ assert.strictEqual(
63
+ element.theme,
64
+ "github-dark",
65
+ "Theme should be github-dark",
66
+ );
51
67
  });
52
68
 
53
69
  it("should render code preview wrapper", async () => {
@@ -93,14 +109,21 @@ describe.skip("Code Preview Component", () => {
93
109
  it.skip("should initialize codePreview property", async () => {
94
110
  // Skipped: Can cause memory issues
95
111
  element = await fixture("grantcodes-code-preview");
96
- assert.strictEqual(element.codePreview, null, "codePreview should be null initially");
112
+ assert.strictEqual(
113
+ element.codePreview,
114
+ null,
115
+ "codePreview should be null initially",
116
+ );
97
117
  });
98
118
 
99
119
  it.skip("should set codePreview in firstUpdated", async () => {
100
120
  // Skipped: Can cause memory issues
101
121
  element = await fixture("grantcodes-code-preview");
102
122
  await element.updateComplete;
103
- assert.ok(element.codePreview !== null, "codePreview should be set after firstUpdated");
123
+ assert.ok(
124
+ element.codePreview !== null,
125
+ "codePreview should be set after firstUpdated",
126
+ );
104
127
  });
105
128
 
106
129
  it.skip("should support various programming languages", async () => {
@@ -114,5 +137,3 @@ describe.skip("Code Preview Component", () => {
114
137
  }
115
138
  });
116
139
  });
117
-
118
-
@@ -1 +1 @@
1
- export * from "./code-preview";
1
+ export * from "./code-preview.js";
@@ -25,6 +25,7 @@ export class GrantCodesContainer extends LitElement {
25
25
  const containerClass = cx("container", {
26
26
  "container--wide": this.align === "wide",
27
27
  "container--full": this.align === "full",
28
+ "container--prose": this.align === "prose",
28
29
  "container--viewport": this.align === "viewport",
29
30
  "container--nopad": this.nopad,
30
31
  });
@@ -4,4 +4,3 @@ export * from "./container.component.js";
4
4
  export default GrantCodesContainer;
5
5
 
6
6
  customElements.define("grantcodes-container", GrantCodesContainer);
7
-
@@ -18,16 +18,18 @@ const meta = {
18
18
  ...argTypes,
19
19
  align: {
20
20
  type: "string",
21
- options: ["default", "wide", "full", "viewport"],
21
+ options: ["default", "wide", "full", "viewport", "prose"],
22
22
  control: { type: "radio" },
23
23
  },
24
24
  },
25
25
  render: (args) =>
26
26
  template(
27
27
  args,
28
- html`<div style="background-color: var(--g-theme-color-background-subtle)">
29
- ${args.content}
30
- </div>`,
28
+ html`<div
29
+ style="background-color: var(--g-theme-color-background-subtle)"
30
+ >
31
+ ${args.content}
32
+ </div>`,
31
33
  ),
32
34
  parameters: {
33
35
  actions: {
@@ -57,3 +59,9 @@ export const ViewportContainer = {
57
59
  align: "viewport",
58
60
  },
59
61
  };
62
+
63
+ export const ProseContainer = {
64
+ args: {
65
+ align: "prose",
66
+ },
67
+ };