ga-toasts 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.
package/README.md CHANGED
@@ -207,26 +207,86 @@ const handle: ToastHandle = toast.show(opts);
207
207
 
208
208
  ---
209
209
 
210
- ## Theming
210
+ ## Configuration & theming
211
211
 
212
- Toggle dark mode with an attribute or class on any ancestor (or let it follow the OS):
212
+ Everything is configurable. Tune the default toaster with `toast.configure(...)` / `toast.theme(...)`, or mint fully **isolated** toasters with `createToaster(config)` each with its own theme, defaults, icons, and mount root.
213
213
 
214
- ```html
215
- <html data-ga-theme="dark"> <!-- or class="ga-theme-dark" -->
214
+ ```js
215
+ import { toast, createToaster } from 'ga-toasts';
216
+
217
+ // Configure the default (global) toaster:
218
+ toast.configure({
219
+ defaults: { position: 'bottom-center', duration: 6000 },
220
+ durations: { error: 10000, success: 2500 }, // per-type auto-close
221
+ theme: { accent: '#ff5c8a', radius: 8, density: 'compact' },
222
+ });
223
+
224
+ // …or a shorthand for just the theme (tokens or a preset name):
225
+ toast.theme('minimal');
226
+ toast.theme({ preset: 'minimal', accent: '#ff5c8a', progress: 'ring' });
227
+
228
+ // An independent toaster (own theme + defaults + DOM root, e.g. shadow DOM):
229
+ const brandToast = createToaster({
230
+ theme: { accent: brand.primary, radius: 6, font: brand.font },
231
+ defaults: { position: 'top-center' },
232
+ root: document.querySelector('#app'),
233
+ });
216
234
  ```
217
235
 
218
- Everything is driven by namespaced CSS variables you can override:
236
+ ### Presets
237
+
238
+ `soft` (default) · `solid` · `minimal` · `sharp` · `material`. Use as `theme: 'minimal'` or `theme: { preset: 'minimal', …overrides }`.
239
+
240
+ ### Theme tokens
241
+
242
+ | Token | Type | Notes |
243
+ |---|---|---|
244
+ | `accent` | color | Primary accent (also the `primary` type). |
245
+ | `colors` | `{ [type]: color }` | Per-type accent colors. |
246
+ | `radius` · `width` · `gap` · `edge` | number \| string | Numbers → px. |
247
+ | `font` | string | Font stack. |
248
+ | `density` | `compact` · `comfortable` · `spacious` | Padding + type scale. |
249
+ | `elevation` | `flat` · `raised` · `floating` | Shadow depth. |
250
+ | `surface` | `glass` · `solid` · `outline` | Card background style. |
251
+ | `accentEdge` | number | Colored leading bar width (0 = none). |
252
+ | `progress` | `bar` · `ring` · `none` | Countdown style (`ring` = circular). |
253
+ | `text` · `textSoft` · `textMuted` · `border` · `shadow` · `chip` · `ease` | | Fine-grained overrides. |
254
+ | `dark` | `Partial<ThemeTokens>` | Overrides applied under the dark theme. |
255
+
256
+ Theme tokens are written as scoped CSS custom properties, so **dark mode still wins** where you don't override it.
257
+
258
+ ### Escape hatches (total control)
259
+
260
+ ```js
261
+ createToaster({
262
+ icons: { success: '<svg>…</svg>', close: '<svg>…</svg>' }, // swap icons (null hides one)
263
+ render: (opts, { id, close }) => myToastElement(opts), // replace the DOM entirely
264
+ injectStyles: false, // headless — ship your own CSS (or import 'ga-toasts/style.css')
265
+ styleNonce: nonce, // strict-CSP nonce on the injected <style>
266
+ stack: { maxVisible: 5, peek: 12, gap: 12, expand: 'always', newestOnTop: true },
267
+ });
268
+ ```
269
+
270
+ ### CSS variables
271
+
272
+ You can also theme purely in CSS — every value is a namespaced `--gat-*` variable:
219
273
 
220
274
  ```css
221
275
  .ga-toast-container {
222
276
  --gat-radius: 12px;
223
277
  --gat-width: 420px;
224
278
  --gat-success: #10b981;
225
- --gat-error: #ef4444;
226
- --gat-info: #3b82f6;
227
279
  }
228
280
  ```
229
281
 
282
+ ### Dark mode
283
+
284
+ Toggle with an attribute or class on any ancestor (or let it follow the OS):
285
+
286
+ ```html
287
+ <html data-ga-theme="dark"> <!-- or class="ga-theme-dark" -->
288
+ ```
289
+
230
290
  ---
231
291
 
232
292
  ## Accessibility
@@ -14,6 +14,13 @@
14
14
  --gat-gap: 14px;
15
15
  --gat-edge: 22px;
16
16
 
17
+ /* Density (padding + type scale) and the optional leading accent bar — all
18
+ driven by the theme() token API; these are the comfortable defaults. */
19
+ --gat-pad-y: 12px;
20
+ --gat-pad-x: 16px;
21
+ --gat-font-size: 0.875rem;
22
+ --gat-accent-edge: 0px;
23
+
17
24
  --gat-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
18
25
  Roboto, "Helvetica Neue", Arial, sans-serif;
19
26
 
@@ -46,8 +53,8 @@
46
53
  [data-ga-theme="dark"] .ga-toast-container,
47
54
  .ga-theme-dark .ga-toast-container,
48
55
  .ga-toast-container.ga-theme-dark {
49
- --gat-surface: rgba(24, 27, 34, 0.82);
50
- --gat-surface-solid: #191c22;
56
+ --gat-surface: rgba(30, 34, 44, 0.72);
57
+ --gat-surface-solid: #22262f;
51
58
  --gat-border: rgba(255, 255, 255, 0.09);
52
59
  --gat-ring: rgba(255, 255, 255, 0.06);
53
60
  --gat-highlight: rgba(255, 255, 255, 0.07);
@@ -130,7 +137,7 @@
130
137
  flex-direction: row;
131
138
  align-items: flex-start;
132
139
  gap: 11px;
133
- padding: 14px 16px;
140
+ padding: var(--gat-pad-y, 12px) var(--gat-pad-x, 16px);
134
141
  color: var(--gat-text);
135
142
  background: var(--gat-surface-solid);
136
143
  border: none;
@@ -147,7 +154,7 @@
147
154
  box-shadow 0.2s ease;
148
155
  will-change: transform, opacity;
149
156
  overflow: hidden;
150
- font-size: 0.875rem;
157
+ font-size: var(--gat-font-size, 0.875rem);
151
158
  line-height: 1.5;
152
159
  -webkit-font-smoothing: antialiased;
153
160
  text-align: start;
@@ -175,11 +182,38 @@
175
182
  -webkit-backdrop-filter: blur(16px) saturate(160%);
176
183
  }
177
184
 
178
- /* No leading accent stripe the colored icon carries the type identity, for a
179
- cleaner, more minimal card (à la Sonner/Linear). */
185
+ /* Outline surface presettransparent card with a defined hairline edge. */
186
+ .ga-toast-outline {
187
+ background: transparent;
188
+ border: 1px solid var(--gat-border);
189
+ box-shadow: var(--gat-shadow);
190
+ backdrop-filter: none;
191
+ -webkit-backdrop-filter: none;
192
+ }
193
+
194
+ /* Optional leading accent bar (theme token `accentEdge`; 0 = hidden). Painted
195
+ as an inset pseudo so it never shifts layout or gets clipped oddly. */
196
+ .ga-toast::before {
197
+ content: "";
198
+ position: absolute;
199
+ inset-block: 0;
200
+ inset-inline-start: 0;
201
+ width: var(--gat-accent-edge, 0px);
202
+ background: var(--gat-accent, var(--gat-info));
203
+ pointer-events: none;
204
+ }
180
205
 
181
206
  .ga-toast-hide { pointer-events: none; }
182
207
 
208
+ /* While a horizontal swipe-to-dismiss is in progress, don't let the drag
209
+ paint a text selection. Text is fully selectable at rest. */
210
+ .ga-toast-swiping,
211
+ .ga-toast-swiping * {
212
+ user-select: none;
213
+ -webkit-user-select: none;
214
+ cursor: grabbing;
215
+ }
216
+
183
217
  /* Per-type accent colors. */
184
218
  .ga-toast-success { --gat-accent: var(--gat-success); }
185
219
  .ga-toast-error { --gat-accent: var(--gat-error); }
@@ -220,6 +254,26 @@
220
254
 
221
255
  .ga-toast-spinner { animation: ga-toast-spin 0.8s linear infinite; }
222
256
 
257
+ /* ---- Circular countdown ring (theme progress: 'ring') ---- */
258
+ .ga-toast-icon-ring { position: relative; width: 2rem; height: 2rem; margin-top: 0; }
259
+ .ga-toast-icon-ring > svg:not(.ga-toast-ring) { width: 1.1rem; height: 1.1rem; }
260
+ .ga-toast-ring {
261
+ position: absolute;
262
+ inset: 0;
263
+ width: 100% !important;
264
+ height: 100% !important;
265
+ transform: rotate(-90deg); /* start the sweep at 12 o'clock */
266
+ overflow: visible;
267
+ }
268
+ .ga-toast-ring-track { fill: none; stroke: var(--gat-chip); stroke-width: 2.4; }
269
+ .ga-toast-ring-bar {
270
+ fill: none;
271
+ stroke: var(--gat-accent, var(--gat-info));
272
+ stroke-width: 2.4;
273
+ stroke-linecap: round;
274
+ /* stroke-dasharray / -dashoffset are driven inline by the engine's timer. */
275
+ }
276
+
223
277
  /* Icons draw themselves in on mount (skipped under reduced motion). */
224
278
  @media (prefers-reduced-motion: no-preference) {
225
279
  .ga-toast-icon .ga-ic-ring {
@@ -468,7 +522,7 @@
468
522
  .ga-toast-xs { padding: 10px 13px; font-size: 0.78rem; }
469
523
  .ga-toast-xs .ga-toast-icon svg { width: 1.05rem; height: 1.05rem; }
470
524
  .ga-toast-xs .ga-toast-message { font-size: 0.78rem; }
471
- .ga-toast-sm { padding: 12px 15px; }
525
+ .ga-toast-sm { padding: 11px 14px; gap: 9px; }
472
526
  .ga-toast-lg { padding: 18px 20px; }
473
527
  .ga-toast-lg .ga-toast-title { font-size: 1rem; }
474
528
  .ga-toast-lg .ga-toast-message { font-size: 0.95rem; }