@quikturn/logos-svelte 1.0.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 ADDED
@@ -0,0 +1,360 @@
1
+ # @quikturn/logos-svelte
2
+
3
+ > Svelte 5 components for the [Quikturn Logos API](https://getquikturn.io) -- drop-in logo display, infinite carousel, and responsive grid.
4
+
5
+ **[Get your API key](https://getquikturn.io)** -- free tier available, no credit card required.
6
+
7
+ ## Features
8
+
9
+ - **`<QuikturnLogo>`** -- single logo image with lazy loading, optional link wrapper
10
+ - **`<QuikturnLogoCarousel>`** -- infinite scrolling logo ticker (horizontal or vertical)
11
+ - **`<QuikturnLogoGrid>`** -- responsive CSS grid of logos
12
+ - **`<QuikturnProvider>`** -- context provider for token and base URL propagation
13
+ - **`createLogoUrl()`** -- reactive rune-based composable that returns a derived logo URL
14
+ - **`getQuikturnContext()`** -- access the injected token and base URL
15
+ - **Zero CSS dependencies** -- inline styles only, no Tailwind or CSS-in-JS required
16
+ - **Svelte 5 native** -- runes (`$state`, `$derived`), snippets for custom rendering
17
+ - **ESM-only** -- built with `@sveltejs/package`, tree-shakeable
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ # pnpm (recommended)
23
+ pnpm add @quikturn/logos-svelte @quikturn/logos svelte
24
+
25
+ # npm
26
+ npm install @quikturn/logos-svelte @quikturn/logos svelte
27
+ ```
28
+
29
+ **Peer dependencies:** `svelte >= 5`, `@quikturn/logos >= 0.1.0`
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Wrap your app with the provider
34
+
35
+ ```svelte
36
+ <!-- +layout.svelte -->
37
+ <script>
38
+ import { QuikturnProvider } from "@quikturn/logos-svelte";
39
+ </script>
40
+
41
+ <QuikturnProvider token="qt_your_publishable_key">
42
+ {@render children()}
43
+ </QuikturnProvider>
44
+ ```
45
+
46
+ ### 2. Use components anywhere
47
+
48
+ ```svelte
49
+ <script>
50
+ import { QuikturnLogo, QuikturnLogoCarousel } from "@quikturn/logos-svelte";
51
+ </script>
52
+
53
+ <!-- Single logo -->
54
+ <QuikturnLogo domain="github.com" size={64} />
55
+
56
+ <!-- Infinite scrolling carousel -->
57
+ <QuikturnLogoCarousel
58
+ domains={["github.com", "stripe.com", "vercel.com", "figma.com"]}
59
+ speed={120}
60
+ logoHeight={28}
61
+ gap={48}
62
+ fadeOut
63
+ />
64
+ ```
65
+
66
+ ## API Reference
67
+
68
+ ### `<QuikturnProvider>`
69
+
70
+ Provides `token` and `baseUrl` to all child components via Svelte context. Individual components can override these values with their own props.
71
+
72
+ ```svelte
73
+ <QuikturnProvider token="qt_abc123" baseUrl="https://custom.api">
74
+ {@render children()}
75
+ </QuikturnProvider>
76
+ ```
77
+
78
+ | Prop | Type | Required | Description |
79
+ |------|------|----------|-------------|
80
+ | `token` | `string` | yes | Publishable API key (`qt_`/`pk_` prefix) |
81
+ | `baseUrl` | `string` | no | Override the Quikturn API base URL |
82
+ | `children` | `Snippet` | yes | Child content |
83
+
84
+ ---
85
+
86
+ ### `<QuikturnLogo>`
87
+
88
+ Renders a single logo `<img>`. Optionally wraps in an `<a>` tag when `href` is provided. Validates `href` to reject `javascript:` and `data:` protocols.
89
+
90
+ ```svelte
91
+ <QuikturnLogo
92
+ domain="stripe.com"
93
+ size={128}
94
+ format="webp"
95
+ greyscale
96
+ theme="dark"
97
+ alt="Stripe"
98
+ href="https://stripe.com"
99
+ loading="lazy"
100
+ class="my-logo"
101
+ style="border-radius: 8px"
102
+ />
103
+ ```
104
+
105
+ | Prop | Type | Default | Description |
106
+ |------|------|---------|-------------|
107
+ | `domain` | `string` | **required** | Domain to fetch logo for |
108
+ | `token` | `string` | from context | Publishable API key |
109
+ | `baseUrl` | `string` | from context | API base URL override |
110
+ | `size` | `number` | -- | Logo width in pixels |
111
+ | `format` | `string` | -- | `"png"`, `"jpeg"`, `"webp"`, `"avif"`, or MIME type |
112
+ | `greyscale` | `boolean` | -- | Greyscale transformation |
113
+ | `theme` | `"light" \| "dark"` | -- | Background-optimized rendering |
114
+ | `alt` | `string` | `"<domain> logo"` | Image alt text |
115
+ | `href` | `string` | -- | Wraps the image in a link |
116
+ | `loading` | `"lazy" \| "eager"` | `"lazy"` | Native image loading strategy |
117
+ | `class` | `string` | -- | CSS class on wrapper element |
118
+ | `style` | `string` | -- | Inline styles on wrapper element |
119
+ | `onload` | `(event: Event) => void` | -- | Fires when the image loads |
120
+ | `onerror` | `(event: Event) => void` | -- | Fires on image load error |
121
+
122
+ ---
123
+
124
+ ### `<QuikturnLogoCarousel>`
125
+
126
+ Infinite scrolling logo ticker powered by `requestAnimationFrame`. Supports horizontal (left/right) and vertical (up/down) scrolling, hover-based speed changes, fade overlays, and per-logo customization.
127
+
128
+ ```svelte
129
+ <QuikturnLogoCarousel
130
+ domains={["github.com", "stripe.com", "vercel.com", "figma.com"]}
131
+ speed={120}
132
+ direction="left"
133
+ logoHeight={28}
134
+ gap={48}
135
+ fadeOut
136
+ fadeOutColor="#f5f5f5"
137
+ pauseOnHover
138
+ scaleOnHover
139
+ width="100%"
140
+ />
141
+ ```
142
+
143
+ #### Using `logos` for per-logo configuration
144
+
145
+ ```svelte
146
+ <QuikturnLogoCarousel
147
+ logos={[
148
+ { domain: "github.com", href: "https://github.com", alt: "GitHub" },
149
+ { domain: "stripe.com", size: 256, greyscale: true },
150
+ { domain: "vercel.com", theme: "dark" },
151
+ ]}
152
+ />
153
+ ```
154
+
155
+ | Prop | Type | Default | Description |
156
+ |------|------|---------|-------------|
157
+ | `domains` | `string[]` | -- | Simple list of domains (use this or `logos`) |
158
+ | `logos` | `LogoConfig[]` | -- | Per-logo configuration objects (use this or `domains`) |
159
+ | `token` | `string` | from context | Publishable API key |
160
+ | `baseUrl` | `string` | from context | API base URL override |
161
+ | `speed` | `number` | `120` | Scroll speed in pixels per second |
162
+ | `direction` | `"left" \| "right" \| "up" \| "down"` | `"left"` | Scroll direction |
163
+ | `pauseOnHover` | `boolean` | `false` | Pause scrolling on mouse hover |
164
+ | `hoverSpeed` | `number` | -- | Custom speed during hover (`0` = pause, overrides `pauseOnHover`) |
165
+ | `logoHeight` | `number` | `28` | Height of each logo image in pixels |
166
+ | `gap` | `number` | `32` | Gap between logos in pixels |
167
+ | `width` | `number \| string` | `"100%"` | Container width (`600`, `"80%"`, etc.) |
168
+ | `fadeOut` | `boolean` | `false` | Show gradient fade overlays at edges |
169
+ | `fadeOutColor` | `string` | `"#ffffff"` | Fade overlay color (match your background) |
170
+ | `scaleOnHover` | `boolean` | `false` | Scale logos on individual hover |
171
+ | `logoSize` | `number` | -- | Default image fetch width for all logos |
172
+ | `logoFormat` | `string` | -- | Default image format for all logos |
173
+ | `logoGreyscale` | `boolean` | -- | Default greyscale setting for all logos |
174
+ | `logoTheme` | `"light" \| "dark"` | -- | Default theme for all logos |
175
+ | `renderItem` | `Snippet<[ResolvedLogo, number]>` | -- | Custom snippet renderer per logo |
176
+ | `class` | `string` | -- | CSS class on root container |
177
+ | `style` | `string` | -- | Inline styles on root container |
178
+ | `ariaLabel` | `string` | `"Company logos"` | Accessible label for the region |
179
+
180
+ #### Custom rendering with snippets
181
+
182
+ ```svelte
183
+ <QuikturnLogoCarousel domains={["github.com", "stripe.com"]}>
184
+ {#snippet renderItem(logo, index)}
185
+ <div style="padding: 12px; background: #f9f9f9; border-radius: 8px">
186
+ <img src={logo.url} alt={logo.alt} height={32} />
187
+ <span style="font-size: 10px; color: #666">{logo.domain}</span>
188
+ </div>
189
+ {/snippet}
190
+ </QuikturnLogoCarousel>
191
+ ```
192
+
193
+ #### `LogoConfig`
194
+
195
+ Used in the `logos` array prop for per-logo customization:
196
+
197
+ ```ts
198
+ interface LogoConfig {
199
+ domain: string; // Required
200
+ href?: string; // Wrap in link
201
+ alt?: string; // Alt text override
202
+ size?: number; // Per-logo image width
203
+ format?: string; // Per-logo format
204
+ greyscale?: boolean; // Per-logo greyscale
205
+ theme?: "light" | "dark";
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ### `<QuikturnLogoGrid>`
212
+
213
+ Responsive CSS grid of logos. Each logo is centered in its grid cell.
214
+
215
+ ```svelte
216
+ <QuikturnLogoGrid
217
+ domains={["github.com", "stripe.com", "vercel.com", "figma.com"]}
218
+ columns={4}
219
+ gap={24}
220
+ ariaLabel="Our partners"
221
+ />
222
+ ```
223
+
224
+ | Prop | Type | Default | Description |
225
+ |------|------|---------|-------------|
226
+ | `domains` | `string[]` | -- | Simple list of domains (use this or `logos`) |
227
+ | `logos` | `LogoConfig[]` | -- | Per-logo configuration objects |
228
+ | `token` | `string` | from context | Publishable API key |
229
+ | `baseUrl` | `string` | from context | API base URL override |
230
+ | `columns` | `number` | `4` | Number of grid columns |
231
+ | `gap` | `number` | `24` | Grid gap in pixels |
232
+ | `logoSize` | `number` | -- | Default image fetch width |
233
+ | `logoFormat` | `string` | -- | Default image format |
234
+ | `logoGreyscale` | `boolean` | -- | Default greyscale |
235
+ | `logoTheme` | `"light" \| "dark"` | -- | Default theme |
236
+ | `renderItem` | `Snippet<[ResolvedLogo, number]>` | -- | Custom snippet renderer per logo |
237
+ | `class` | `string` | -- | CSS class on grid container |
238
+ | `ariaLabel` | `string` | `"Company logos"` | Accessible label for the region |
239
+
240
+ ---
241
+
242
+ ### `createLogoUrl(getDomain, getOptions?)`
243
+
244
+ Reactive composable that returns a derived logo URL using Svelte 5 runes. Pulls `token` and `baseUrl` from context unless overridden. Arguments are getter functions for reactivity.
245
+
246
+ ```svelte
247
+ <script>
248
+ import { createLogoUrl } from "@quikturn/logos-svelte";
249
+
250
+ let domain = $state("github.com");
251
+ const { url } = createLogoUrl(() => domain, () => ({ size: 256, format: "webp" }));
252
+ </script>
253
+
254
+ <input bind:value={domain} placeholder="Enter a domain" />
255
+ <img src={url} alt="{domain} logo" />
256
+ ```
257
+
258
+ | Parameter | Type | Description |
259
+ |-----------|------|-------------|
260
+ | `getDomain` | `() => string` | Getter returning the domain |
261
+ | `getOptions` | `() => LogoOptions & { token?: string; baseUrl?: string }` | Getter returning options |
262
+
263
+ **Returns:** `{ readonly url: string }` -- object with a reactive `url` getter
264
+
265
+ ---
266
+
267
+ ### `getQuikturnContext()`
268
+
269
+ Returns the token and base URL injected by `<QuikturnProvider>`. Returns `undefined` if no provider is present.
270
+
271
+ ```svelte
272
+ <script>
273
+ import { getQuikturnContext } from "@quikturn/logos-svelte";
274
+
275
+ const ctx = getQuikturnContext();
276
+ // ctx?.token, ctx?.baseUrl
277
+ </script>
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Examples
283
+
284
+ ### Logo Wall (Marketing Page)
285
+
286
+ ```svelte
287
+ <script>
288
+ import { QuikturnLogoCarousel } from "@quikturn/logos-svelte";
289
+
290
+ const partners = [
291
+ "github.com", "stripe.com", "vercel.com", "figma.com",
292
+ "linear.app", "notion.so", "slack.com", "discord.com",
293
+ ];
294
+ </script>
295
+
296
+ <h2>Trusted by industry leaders</h2>
297
+ <QuikturnLogoCarousel
298
+ domains={partners}
299
+ speed={80}
300
+ logoHeight={32}
301
+ gap={64}
302
+ fadeOut
303
+ pauseOnHover
304
+ />
305
+ ```
306
+
307
+ ### Partner Grid with Links
308
+
309
+ ```svelte
310
+ <script>
311
+ import { QuikturnLogoGrid } from "@quikturn/logos-svelte";
312
+
313
+ const partners = [
314
+ { domain: "github.com", href: "https://github.com", alt: "GitHub" },
315
+ { domain: "stripe.com", href: "https://stripe.com", alt: "Stripe" },
316
+ { domain: "vercel.com", href: "https://vercel.com", alt: "Vercel" },
317
+ { domain: "figma.com", href: "https://figma.com", alt: "Figma" },
318
+ ];
319
+ </script>
320
+
321
+ <QuikturnLogoGrid logos={partners} columns={2} gap={32} />
322
+ ```
323
+
324
+ ### Vertical Carousel
325
+
326
+ ```svelte
327
+ <div style="height: 240px">
328
+ <QuikturnLogoCarousel
329
+ domains={["github.com", "stripe.com", "vercel.com"]}
330
+ direction="up"
331
+ speed={60}
332
+ logoHeight={24}
333
+ />
334
+ </div>
335
+ ```
336
+
337
+ ### Reactive Domain Input
338
+
339
+ ```svelte
340
+ <script>
341
+ import { createLogoUrl } from "@quikturn/logos-svelte";
342
+
343
+ let domain = $state("github.com");
344
+ const { url } = createLogoUrl(() => domain, () => ({ size: 256, format: "webp" }));
345
+ </script>
346
+
347
+ <input bind:value={domain} placeholder="Enter a domain" />
348
+ <img src={url} alt="{domain} logo" />
349
+ ```
350
+
351
+ ## Resources
352
+
353
+ - **[Quikturn website](https://getquikturn.io)** -- sign up, manage keys, explore the API
354
+ - **[Dashboard](https://getquikturn.io/dashboard)** -- usage analytics, key management, plan upgrades
355
+ - **[Pricing](https://getquikturn.io/pricing)** -- free tier, pro, and enterprise plans
356
+ - **[Core SDK docs](https://www.npmjs.com/package/@quikturn/logos)** -- `@quikturn/logos` URL builder, browser client, server client
357
+
358
+ ## License
359
+
360
+ MIT -- built by [Quikturn](https://getquikturn.io)
@@ -0,0 +1,65 @@
1
+ <script lang="ts">
2
+ import { logoUrl } from "@quikturn/logos";
3
+ import { getQuikturnContext } from "./context.svelte.js";
4
+ import { fireBeacon } from "./beacon.js";
5
+ import { isValidHref } from "./validate-href.js";
6
+ import type { QuikturnLogoProps } from "./types.js";
7
+ import { onMount } from "svelte";
8
+
9
+ let {
10
+ domain,
11
+ token,
12
+ baseUrl,
13
+ size,
14
+ format,
15
+ greyscale,
16
+ theme,
17
+ alt,
18
+ href,
19
+ class: className,
20
+ style,
21
+ loading = "lazy",
22
+ onerror,
23
+ onload,
24
+ }: QuikturnLogoProps = $props();
25
+
26
+ const ctx = getQuikturnContext();
27
+
28
+ const effectiveToken = $derived(token ?? ctx?.token ?? "");
29
+ const effectiveBaseUrl = $derived(baseUrl ?? ctx?.baseUrl);
30
+
31
+ const src = $derived(
32
+ logoUrl(domain, {
33
+ token: effectiveToken || undefined,
34
+ size,
35
+ format,
36
+ greyscale,
37
+ theme,
38
+ baseUrl: effectiveBaseUrl,
39
+ }),
40
+ );
41
+
42
+ const altText = $derived(alt ?? `${domain} logo`);
43
+
44
+ onMount(() => {
45
+ if (effectiveToken) fireBeacon(effectiveToken);
46
+ });
47
+ </script>
48
+
49
+ {#if href && isValidHref(href)}
50
+ <a
51
+ {href}
52
+ target="_blank"
53
+ rel="noopener noreferrer"
54
+ class={className}
55
+ {style}
56
+ >
57
+ <img src={src} alt={altText} {loading} {onerror} {onload} />
58
+ </a>
59
+ {:else if className || style}
60
+ <span class={className} {style}>
61
+ <img src={src} alt={altText} {loading} {onerror} {onload} />
62
+ </span>
63
+ {:else}
64
+ <img src={src} alt={altText} {loading} {onerror} {onload} />
65
+ {/if}
@@ -0,0 +1,5 @@
1
+ import type { QuikturnLogoProps } from "./types.js";
2
+ declare const QuikturnLogo: import("svelte").Component<QuikturnLogoProps, {}, "">;
3
+ type QuikturnLogo = ReturnType<typeof QuikturnLogo>;
4
+ export default QuikturnLogo;
5
+ //# sourceMappingURL=QuikturnLogo.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuikturnLogo.svelte.d.ts","sourceRoot":"","sources":["../src/lib/QuikturnLogo.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiEpD,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}