@jamwidgets/astro 0.6.4 → 0.6.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/README.md CHANGED
@@ -234,6 +234,7 @@ import Subscribe from "@jamwidgets/astro/Subscribe";
234
234
  - `endpoint` - Base URL (default: `https://jamwidgets.com`)
235
235
  - `buttonText` - Submit button text (default: `'Subscribe'`)
236
236
  - `placeholder` - Email input placeholder
237
+ - `label` - Accessible name for the email input (default: `'Email address'`)
237
238
  - `successMessage` - Custom success message
238
239
  - `theme` - `'light'` | `'dark'` | `'auto'` (default: `'light'`)
239
240
  - `class` - Additional CSS class
@@ -317,6 +318,10 @@ Set semantic CSS custom properties on a wrapper or the widget itself to match yo
317
318
 
318
319
  Status colors can be customized with `--jamwidgets-color-success`, `--jamwidgets-color-error`, and `--jamwidgets-color-notice`. Components adapt to their container width, so subscribe, waitlist, comments, and feedback controls stack when embedded in a narrow column.
319
320
 
321
+ ### Cascade layers and Tailwind
322
+
323
+ Widget styles live in a `jamwidgets` cascade layer. Every component declares the order `@layer theme, base, components, utilities, jamwidgets;` before its rules, so the widgets rank above Tailwind v4's layers (including Preflight in `base`) whichever stylesheet the browser sees first. Your unlayered CSS still overrides the widgets. If your site declares its own layer order, keep `jamwidgets` after `base` or Preflight will reset the widgets' padding, borders and buttons.
324
+
320
325
  ## License
321
326
 
322
327
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jamwidgets/astro",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Astro components and content loaders for Jamwidgets",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,7 +60,7 @@
60
60
  ],
61
61
  "license": "MIT",
62
62
  "dependencies": {
63
- "@jamwidgets/core": "^0.5.3"
63
+ "@jamwidgets/core": "^0.6.0"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "astro": "^5.9.0 || ^6.0.0 || ^7.0.0"
@@ -151,6 +151,7 @@ import Theme from "./Theme.astro";
151
151
  </script>
152
152
 
153
153
  <style is:global>
154
+ @layer theme, base, components, utilities, jamwidgets;
154
155
  @layer jamwidgets {
155
156
  .jamwidgets-announcements {
156
157
  display: flex;
@@ -262,6 +262,7 @@ import Theme from "./Theme.astro";
262
262
  </script>
263
263
 
264
264
  <style is:global>
265
+ @layer theme, base, components, utilities, jamwidgets;
265
266
  @layer jamwidgets {
266
267
  .jamwidgets-comments {
267
268
  color: var(--jamwidgets-text-color);
package/src/Embed.astro CHANGED
@@ -64,7 +64,7 @@ import Theme from "./Theme.astro";
64
64
  userVotes?: string[];
65
65
  }
66
66
 
67
- import { getVisitorId } from "@jamwidgets/core";
67
+ import { apiError, getVisitorId } from "@jamwidgets/core";
68
68
 
69
69
  class JamwidgetsEmbed extends HTMLElement {
70
70
  connectedCallback() {
@@ -271,8 +271,7 @@ import Theme from "./Theme.astro";
271
271
  });
272
272
 
273
273
  if (!response.ok) {
274
- const error = await response.json();
275
- throw new Error(error.message || "Failed to vote");
274
+ throw await apiError(response, "Failed to vote");
276
275
  }
277
276
 
278
277
  const data = await response.json();
@@ -312,6 +311,7 @@ import Theme from "./Theme.astro";
312
311
  </script>
313
312
 
314
313
  <style is:global>
314
+ @layer theme, base, components, utilities, jamwidgets;
315
315
  @layer jamwidgets {
316
316
  .jamwidgets-poll {
317
317
  font-family: inherit;
@@ -3,7 +3,7 @@
3
3
  * Jamwidgets Feedback Component
4
4
  *
5
5
  * Displays a feedback form for collecting user suggestions.
6
- * Supports different feedback types (bug, feature, improvement, other).
6
+ * Supports the API's feedback types: feature, bug and general.
7
7
  *
8
8
  * @example
9
9
  * <Feedback siteKey={import.meta.env.JAMWIDGETS_SITE_KEY} />
@@ -62,11 +62,7 @@ import Theme from "./Theme.astro";
62
62
  <span>Bug</span>
63
63
  </label>
64
64
  <label class="jamwidgets-feedback-type">
65
- <input type="radio" name="feedbackType" value="improvement" />
66
- <span>Improvement</span>
67
- </label>
68
- <label class="jamwidgets-feedback-type">
69
- <input type="radio" name="feedbackType" value="other" />
65
+ <input type="radio" name="feedbackType" value="general" />
70
66
  <span>Other</span>
71
67
  </label>
72
68
  </div>
@@ -90,7 +86,7 @@ import Theme from "./Theme.astro";
90
86
  </div>
91
87
 
92
88
  <script>
93
- import { getVisitorId } from "@jamwidgets/core";
89
+ import { apiError, getVisitorId } from "@jamwidgets/core";
94
90
 
95
91
  document.querySelectorAll("[data-jamwidgets-feedback]").forEach((container) => {
96
92
  const endpoint = (container as HTMLElement).dataset.endpoint;
@@ -117,7 +113,7 @@ import Theme from "./Theme.astro";
117
113
  if (!content) return;
118
114
 
119
115
  const typeInput = form.querySelector('input[name="feedbackType"]:checked') as HTMLInputElement | null;
120
- const feedbackType = typeInput?.value || "other";
116
+ const feedbackType = typeInput?.value || "general";
121
117
 
122
118
  submitBtn.disabled = true;
123
119
  submitBtn.textContent = "Sending...";
@@ -132,8 +128,7 @@ import Theme from "./Theme.astro";
132
128
  });
133
129
 
134
130
  if (!response.ok) {
135
- const error = await response.json();
136
- throw new Error(error.message || "Failed to submit feedback");
131
+ throw await apiError(response, "Failed to submit feedback");
137
132
  }
138
133
 
139
134
  textarea.value = "";
@@ -163,6 +158,7 @@ import Theme from "./Theme.astro";
163
158
  </script>
164
159
 
165
160
  <style is:global>
161
+ @layer theme, base, components, utilities, jamwidgets;
166
162
  @layer jamwidgets {
167
163
  .jamwidgets-feedback {
168
164
  font-family: inherit;
package/src/Form.astro CHANGED
@@ -67,7 +67,7 @@ import Theme from "./Theme.astro";
67
67
  </form>
68
68
 
69
69
  <script>
70
- import { generateUUID, getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
70
+ import { apiError, generateUUID, getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
71
71
 
72
72
  interface FormSubmitResponse {
73
73
  success: boolean;
@@ -145,8 +145,7 @@ import Theme from "./Theme.astro";
145
145
  });
146
146
 
147
147
  if (!response.ok) {
148
- const errorText = await response.text();
149
- throw new Error(errorText || "Submission failed");
148
+ throw await apiError(response, "Submission failed");
150
149
  }
151
150
 
152
151
  const result: FormSubmitResponse = await response.json();
@@ -193,6 +192,7 @@ import Theme from "./Theme.astro";
193
192
  </script>
194
193
 
195
194
  <style is:global>
195
+ @layer theme, base, components, utilities, jamwidgets;
196
196
  @layer jamwidgets {
197
197
  .jamwidgets-form-message {
198
198
  margin-top: 1rem;
package/src/Poll.astro CHANGED
@@ -89,7 +89,7 @@ import Theme from "./Theme.astro";
89
89
  userVotes?: string[];
90
90
  }
91
91
 
92
- import { getVisitorId } from "@jamwidgets/core";
92
+ import { apiError, getVisitorId } from "@jamwidgets/core";
93
93
 
94
94
  document.querySelectorAll("[data-jamwidgets-poll]").forEach((container) => {
95
95
  const endpoint = (container as HTMLElement).dataset.endpoint;
@@ -255,8 +255,7 @@ import Theme from "./Theme.astro";
255
255
  });
256
256
 
257
257
  if (!response.ok) {
258
- const error = await response.json();
259
- throw new Error(error.message || "Failed to vote");
258
+ throw await apiError(response, "Failed to vote");
260
259
  }
261
260
 
262
261
  const data = await response.json();
@@ -293,6 +292,7 @@ import Theme from "./Theme.astro";
293
292
  </script>
294
293
 
295
294
  <style is:global>
295
+ @layer theme, base, components, utilities, jamwidgets;
296
296
  @layer jamwidgets {
297
297
  .jamwidgets-poll {
298
298
  font-family: inherit;
@@ -221,6 +221,7 @@ import Theme from "./Theme.astro";
221
221
  </script>
222
222
 
223
223
  <style is:global>
224
+ @layer theme, base, components, utilities, jamwidgets;
224
225
  @layer jamwidgets {
225
226
  .jamwidgets-reactions {
226
227
  display: flex;
@@ -26,6 +26,8 @@ interface Props {
26
26
  buttonText?: string;
27
27
  /** Email input placeholder (default: 'your@email.com') */
28
28
  placeholder?: string;
29
+ /** Accessible name for the email input (default: 'Email address') */
30
+ label?: string;
29
31
  /** Success message override (default: uses server response) */
30
32
  successMessage?: string;
31
33
  /** Theme preset. Defaults to inheriting the host page. */
@@ -42,6 +44,7 @@ const {
42
44
  endpoint = DEFAULT_ENDPOINT,
43
45
  buttonText = "Subscribe",
44
46
  placeholder = "your@email.com",
47
+ label = "Email address",
45
48
  successMessage,
46
49
  theme = "inherit",
47
50
  class: className = "",
@@ -68,6 +71,7 @@ import Theme from "./Theme.astro";
68
71
  name="email"
69
72
  required
70
73
  placeholder={placeholder}
74
+ aria-label={label}
71
75
  class="jamwidgets-subscribe-input"
72
76
  autocomplete="email"
73
77
  />
@@ -93,7 +97,7 @@ import Theme from "./Theme.astro";
93
97
  message: string;
94
98
  }
95
99
 
96
- import { getVisitorId } from "@jamwidgets/core";
100
+ import { apiError, getVisitorId } from "@jamwidgets/core";
97
101
 
98
102
  document.querySelectorAll("[data-jamwidgets-subscribe]").forEach((form) => {
99
103
  form.addEventListener("submit", async (e) => {
@@ -141,8 +145,7 @@ import Theme from "./Theme.astro";
141
145
  });
142
146
 
143
147
  if (!response.ok) {
144
- const errorText = await response.text();
145
- throw new Error(errorText || "Subscription failed");
148
+ throw await apiError(response, "Subscription failed");
146
149
  }
147
150
 
148
151
  const result: SubscribeResponse = await response.json();
@@ -190,6 +193,7 @@ import Theme from "./Theme.astro";
190
193
  </script>
191
194
 
192
195
  <style is:global>
196
+ @layer theme, base, components, utilities, jamwidgets;
193
197
  @layer jamwidgets {
194
198
  .jamwidgets-subscribe {
195
199
  position: relative;
@@ -71,7 +71,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
71
71
  </form>
72
72
 
73
73
  <script>
74
- import { getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
74
+ import { apiError, getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
75
75
 
76
76
  interface SubscribeResponse {
77
77
  success: boolean;
@@ -114,8 +114,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
114
114
  });
115
115
 
116
116
  if (!response.ok) {
117
- const errorText = await response.text();
118
- throw new Error(errorText || "Subscription failed");
117
+ throw await apiError(response, "Subscription failed");
119
118
  }
120
119
 
121
120
  const result: SubscribeResponse = await response.json();
package/src/Theme.astro CHANGED
@@ -1,4 +1,11 @@
1
1
  <style is:global>
2
+ /* Whichever stylesheet names a layer first fixes its order. If these widget
3
+ styles load before a Tailwind site's CSS, `jamwidgets` would otherwise
4
+ become the lowest layer and Tailwind's base reset would strip widget
5
+ input borders. Naming Tailwind's layers first, and `jamwidgets` last,
6
+ gives the same order whichever CSS loads first. Every widget repeats
7
+ this statement; unlayered site CSS still wins. */
8
+ @layer theme, base, components, utilities, jamwidgets;
2
9
  @layer jamwidgets {
3
10
  :where(
4
11
  .jamwidgets-announcements,
package/src/Views.astro CHANGED
@@ -149,6 +149,7 @@ import Theme from "./Theme.astro";
149
149
  </script>
150
150
 
151
151
  <style is:global>
152
+ @layer theme, base, components, utilities, jamwidgets;
152
153
  @layer jamwidgets {
153
154
  .jamwidgets-views {
154
155
  display: inline-flex;
@@ -17,6 +17,8 @@ interface Props {
17
17
  theme?: "inherit" | "light" | "dark" | "auto";
18
18
  /** Placeholder text for the email input (default: 'Enter your email') */
19
19
  placeholder?: string;
20
+ /** Accessible name for the email input (default: 'Email address') */
21
+ label?: string;
20
22
  /** Button text (default: 'Join Waitlist') */
21
23
  buttonText?: string;
22
24
  /** Success message (default: "You're on the list!") */
@@ -33,6 +35,7 @@ const {
33
35
  endpoint = DEFAULT_ENDPOINT,
34
36
  theme = "inherit",
35
37
  placeholder = "Enter your email",
38
+ label = "Email address",
36
39
  buttonText = "Join Waitlist",
37
40
  successMessage = "You're on the list!",
38
41
  class: className = "",
@@ -59,6 +62,8 @@ import Theme from "./Theme.astro";
59
62
  name="email"
60
63
  class="jamwidgets-waitlist-input"
61
64
  placeholder={placeholder}
65
+ aria-label={label}
66
+ autocomplete="email"
62
67
  required
63
68
  />
64
69
  <button type="submit" class="jamwidgets-waitlist-submit">
@@ -71,7 +76,7 @@ import Theme from "./Theme.astro";
71
76
  </div>
72
77
 
73
78
  <script>
74
- import { getVisitorId } from "@jamwidgets/core";
79
+ import { apiError, getVisitorId } from "@jamwidgets/core";
75
80
 
76
81
  document.querySelectorAll("[data-jamwidgets-waitlist]").forEach((container) => {
77
82
  const endpoint = (container as HTMLElement).dataset.endpoint;
@@ -111,8 +116,7 @@ import Theme from "./Theme.astro";
111
116
  });
112
117
 
113
118
  if (!response.ok) {
114
- const error = await response.json();
115
- throw new Error(error.message || "Failed to join waitlist");
119
+ throw await apiError(response, "Failed to join waitlist");
116
120
  }
117
121
 
118
122
  // Hide form and show success
@@ -143,6 +147,7 @@ import Theme from "./Theme.astro";
143
147
  </script>
144
148
 
145
149
  <style is:global>
150
+ @layer theme, base, components, utilities, jamwidgets;
146
151
  @layer jamwidgets {
147
152
  .jamwidgets-waitlist {
148
153
  font-family: inherit;