spoko-design-system 1.8.1 → 1.9.1

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.
@@ -27,6 +27,68 @@ const navItems = [
27
27
  icon: 'streamline-freehand-color:layouts-array-1',
28
28
  },
29
29
  ];
30
+
31
+ const features = [
32
+ {
33
+ title: 'Astro-Powered',
34
+ icon: 'vscode-icons:file-type-light-astro',
35
+ description:
36
+ 'Built with <a href="https://astro.build/" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-800 underline transition-colors">Astro</a> for lightning-fast performance and seamless integration with modern frameworks.',
37
+ },
38
+ {
39
+ title: 'Rich Components',
40
+ icon: 'streamline-freehand-color:data-transfer-document-module',
41
+ description:
42
+ '20+ production-ready components including buttons, modals, carousels, and specialized automotive elements.',
43
+ },
44
+ {
45
+ title: 'UnoCSS Styling',
46
+ icon: 'vscode-icons:file-type-unocss',
47
+ description:
48
+ 'Atomic CSS approach with <a href="https://unocss.dev/" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-800 underline transition-colors">UnoCSS</a> for flexible, maintainable styling and consistent design tokens.',
49
+ },
50
+ {
51
+ title: 'Vue Integration',
52
+ icon: 'vscode-icons:file-type-vue',
53
+ description:
54
+ 'Seamless <a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-800 underline transition-colors">Vue 3</a> component integration with TypeScript support for interactive elements.',
55
+ },
56
+ {
57
+ title: 'Design Patterns',
58
+ icon: 'streamline-freehand-color:design-process-drawing-board',
59
+ description:
60
+ 'Proven patterns and layouts for common use cases, from landing pages to product catalogs.',
61
+ },
62
+ {
63
+ title: 'Developer Ready',
64
+ icon: 'streamline-freehand-color:app-window-source-code',
65
+ description:
66
+ 'Complete documentation, TypeScript support, and easy npm installation for quick project setup.',
67
+ },
68
+ ];
69
+
70
+ const exampleSites = [
71
+ {
72
+ url: 'https://catalog.polo.blue',
73
+ title: 'catalog.polo.blue',
74
+ description: 'Car parts catalog',
75
+ },
76
+ {
77
+ url: 'https://polo.blue',
78
+ title: 'polo.blue',
79
+ description: 'Polo 6R DIY workshop & guides',
80
+ },
81
+ {
82
+ url: 'https://sale.polo.blue/',
83
+ title: 'sale.polo.blue',
84
+ description: 'Used car parts marketplace',
85
+ },
86
+ {
87
+ url: 'https://polo6r.pl',
88
+ title: 'polo6r.pl',
89
+ description: 'Polo V Manual',
90
+ },
91
+ ];
30
92
  ---
31
93
 
32
94
  <Layout>
@@ -61,13 +123,13 @@ const navItems = [
61
123
  >
62
124
  <Headline
63
125
  as="h2"
64
- textSize="2xl"
126
+ class="text-3xl"
65
127
  underline={false}
66
128
  >
67
129
  <Icon
68
130
  name={icon}
69
131
  aria-hidden="true"
70
- class="text-blue-400 mr-2 text-4xl"
132
+ class="text-blue-400 mr-3 text-4xl"
71
133
  />
72
134
  {title}
73
135
  </Headline>
@@ -83,9 +145,8 @@ const navItems = [
83
145
  <div class="text-center mb-12">
84
146
  <Headline
85
147
  as="h2"
86
- textSize="3xl"
148
+ class="text-3xl md:text-4xl text-gray-900 mb-6"
87
149
  underline="center"
88
- class="text-gray-900 mb-6"
89
150
  >
90
151
  Why Spoko Design System?
91
152
  </Headline>
@@ -96,140 +157,28 @@ const navItems = [
96
157
  </div>
97
158
 
98
159
  <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
99
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
100
- <Headline
101
- as="h3"
102
- textSize="xl"
103
- underline={false}
104
- class="mb-3 text-gray-900 flex items-center justify-center"
105
- >
106
- <Icon
107
- name="vscode-icons:file-type-light-astro"
108
- aria-hidden="true"
109
- class="text-4xl text-blue-400 mr-3"
110
- />
111
- Astro-Powered
112
- </Headline>
113
- <p class="text-gray-600">
114
- Built with <a
115
- href="https://astro.build/"
116
- target="_blank"
117
- rel="noopener noreferrer"
118
- class="text-blue-600 hover:text-blue-800 underline transition-colors"
119
- >Astro</a
120
- > for lightning-fast performance and seamless integration with modern frameworks.
121
- </p>
122
- </div>
123
-
124
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
125
- <Headline
126
- as="h3"
127
- textSize="xl"
128
- underline={false}
129
- class="mb-3 text-gray-900 flex items-center justify-center"
130
- >
131
- <Icon
132
- name="streamline-freehand-color:data-transfer-document-module"
133
- aria-hidden="true"
134
- class="text-4xl text-blue-400 mr-3"
135
- />
136
- Rich Components
137
- </Headline>
138
- <p class="text-gray-600">
139
- 20+ production-ready components including buttons, modals, carousels, and specialized
140
- automotive elements.
141
- </p>
142
- </div>
143
-
144
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
145
- <Headline
146
- as="h3"
147
- textSize="xl"
148
- underline={false}
149
- class="mb-3 text-gray-900 flex items-center justify-center"
150
- >
151
- <Icon
152
- name="vscode-icons:file-type-unocss"
153
- aria-hidden="true"
154
- class="text-4xl text-blue-400 mr-3"
155
- />
156
- UnoCSS Styling
157
- </Headline>
158
- <p class="text-gray-600">
159
- Atomic CSS approach with <a
160
- href="https://unocss.dev/"
161
- target="_blank"
162
- rel="noopener noreferrer"
163
- class="text-blue-600 hover:text-blue-800 underline transition-colors"
164
- >UnoCSS</a
165
- > for flexible, maintainable styling and consistent design tokens.
166
- </p>
167
- </div>
168
-
169
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
170
- <Headline
171
- as="h3"
172
- textSize="xl"
173
- underline={false}
174
- class="mb-3 text-gray-900 flex items-center justify-center"
175
- >
176
- <Icon
177
- name="vscode-icons:file-type-vue"
178
- aria-hidden="true"
179
- class="text-4xl text-blue-400 mr-3"
180
- />
181
- Vue Integration
182
- </Headline>
183
- <p class="text-gray-600">
184
- Seamless <a
185
- href="https://vuejs.org/"
186
- target="_blank"
187
- rel="noopener noreferrer"
188
- class="text-blue-600 hover:text-blue-800 underline transition-colors"
189
- >Vue 3</a
190
- > component integration with TypeScript support for interactive elements.
191
- </p>
192
- </div>
193
-
194
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
195
- <Headline
196
- as="h3"
197
- textSize="xl"
198
- underline={false}
199
- class="mb-3 text-gray-900 flex items-center justify-center"
200
- >
201
- <Icon
202
- name="streamline-freehand-color:design-process-drawing-board"
203
- aria-hidden="true"
204
- class="text-4xl text-blue-400 mr-3"
205
- />
206
- Design Patterns
207
- </Headline>
208
- <p class="text-gray-600">
209
- Proven patterns and layouts for common use cases, from landing pages to product
210
- catalogs.
211
- </p>
212
- </div>
213
-
214
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
215
- <Headline
216
- as="h3"
217
- textSize="xl"
218
- underline={false}
219
- class="mb-3 text-gray-900 flex items-center justify-center"
220
- >
221
- <Icon
222
- name="streamline-freehand-color:app-window-source-code"
223
- aria-hidden="true"
224
- class="text-4xl text-blue-400 mr-3"
225
- />
226
- Developer Ready
227
- </Headline>
228
- <p class="text-gray-600">
229
- Complete documentation, TypeScript support, and easy npm installation for quick project
230
- setup.
231
- </p>
232
- </div>
160
+ {
161
+ features.map(({ title, icon, description }) => (
162
+ <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
163
+ <Headline
164
+ as="h3"
165
+ class="text-2xl mb-3 text-gray-900 flex items-center justify-center"
166
+ underline={false}
167
+ >
168
+ <Icon
169
+ name={icon}
170
+ aria-hidden="true"
171
+ class="text-4xl text-blue-400 mr-3"
172
+ />
173
+ {title}
174
+ </Headline>
175
+ <p
176
+ class="text-gray-600"
177
+ set:html={description}
178
+ />
179
+ </div>
180
+ ))
181
+ }
233
182
  </div>
234
183
  </section>
235
184
 
@@ -252,8 +201,7 @@ const navItems = [
252
201
  <Headline
253
202
  underline
254
203
  as="h2"
255
- class="text-gray-900 mb-8"
256
- textSize="3xl"
204
+ class="text-3xl text-gray-900 mb-8"
257
205
  >
258
206
  <Icon
259
207
  name="streamline-freehand-color:archive-box"
@@ -313,8 +261,7 @@ const navItems = [
313
261
  <Headline
314
262
  underline
315
263
  as="h2"
316
- class="text-gray-900 mb-8"
317
- textSize="3xl"
264
+ class="text-3xl text-gray-900 mb-8"
318
265
  >
319
266
  <Icon
320
267
  name="streamline-freehand-color:coding-files-network-folder"
@@ -326,73 +273,25 @@ const navItems = [
326
273
 
327
274
  <!-- Example Sites Grid -->
328
275
  <div class="grid gap-4 mb-6">
329
- <a
330
- href="https://catalog.polo.blue"
331
- target="_blank"
332
- rel="noopener noreferrer"
333
- class="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg hover:border-blue-400 hover:shadow-md transition-all group"
334
- >
335
- <div class="text-left">
336
- <h4 class="font-semibold text-gray-900 group-hover:text-blue-600">
337
- catalog.polo.blue
338
- </h4>
339
- <p class="text-sm text-gray-600">Car parts catalog</p>
340
- </div>
341
- <Icon
342
- name="lucide:link"
343
- class="text-gray-400 group-hover:text-blue-400"
344
- />
345
- </a>
346
-
347
- <a
348
- href="https://polo.blue"
349
- target="_blank"
350
- rel="noopener noreferrer"
351
- class="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg hover:border-blue-400 hover:shadow-md transition-all group"
352
- >
353
- <div class="text-left">
354
- <h4 class="font-semibold text-gray-900 group-hover:text-blue-600">polo.blue</h4>
355
- <p class="text-sm text-gray-600">Polo 6R DIY workshop & guides</p>
356
- </div>
357
- <Icon
358
- name="lucide:link"
359
- class="text-gray-400 group-hover:text-blue-400"
360
- />
361
- </a>
362
-
363
- <a
364
- href="https://sale.polo.blue/"
365
- target="_blank"
366
- rel="noopener noreferrer"
367
- class="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg hover:border-blue-400 hover:shadow-md transition-all group"
368
- >
369
- <div class="text-left">
370
- <h4 class="font-semibold text-gray-900 group-hover:text-blue-600">
371
- sale.polo.blue
372
- </h4>
373
- <p class="text-sm text-gray-600">Used car parts marketplace</p>
374
- </div>
375
- <Icon
376
- name="lucide:link"
377
- class="text-gray-400 group-hover:text-blue-400"
378
- />
379
- </a>
380
-
381
- <a
382
- href="https://polo6r.pl"
383
- target="_blank"
384
- rel="noopener noreferrer"
385
- class="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg hover:border-blue-400 hover:shadow-md transition-all group"
386
- >
387
- <div class="text-left">
388
- <h4 class="font-semibold text-gray-900 group-hover:text-blue-600">polo6r.pl</h4>
389
- <p class="text-sm text-gray-600">Polo V Manual</p>
390
- </div>
391
- <Icon
392
- name="lucide:link"
393
- class="text-gray-400 group-hover:text-blue-400"
394
- />
395
- </a>
276
+ {
277
+ exampleSites.map(({ url, title, description }) => (
278
+ <a
279
+ href={url}
280
+ target="_blank"
281
+ rel="noopener noreferrer"
282
+ class="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg hover:border-blue-400 hover:shadow-md transition-all group"
283
+ >
284
+ <div class="text-left">
285
+ <h4 class="font-semibold text-gray-900 group-hover:text-blue-600">{title}</h4>
286
+ <p class="text-sm text-gray-600">{description}</p>
287
+ </div>
288
+ <Icon
289
+ name="lucide:link"
290
+ class="text-gray-400 group-hover:text-blue-400"
291
+ />
292
+ </a>
293
+ ))
294
+ }
396
295
  </div>
397
296
 
398
297
  <p class="text-gray-600 text-sm">
@@ -1,46 +1,51 @@
1
- import tippy from 'tippy.js';
2
- import 'tippy.js/dist/tippy.css';
3
- import '../styles/tippy-theme.css';
4
-
5
1
  /**
6
- * Global Tooltip Initializer
7
- * Automatically enhances all elements with data-tippy-content attribute
8
- * Works with static HTML - perfect for SEO and Astro static pages
2
+ * Global tooltip delegation script for SDS
3
+ * Uses Tippy.js delegation pattern for performance
4
+ * Handles tooltips for:
5
+ * - Engine codes (.engine-code)
6
+ * - PR codes (.btn-prcode)
7
+ * - Any other elements with data-tippy-content
9
8
  */
10
9
 
11
- function initTooltips() {
12
- // Destroy existing tooltips to avoid duplicates
13
- document.querySelectorAll('[data-tippy-content]').forEach((el: any) => {
14
- if (el._tippy) {
15
- el._tippy.destroy();
16
- }
17
- });
10
+ import { delegate } from 'tippy.js';
18
11
 
19
- // Initialize tooltips for all elements with data-tippy-content
20
- tippy('[data-tippy-content]', {
12
+ /**
13
+ * Initialize tooltips with delegation pattern
14
+ * Call this once in your layout after page load
15
+ */
16
+ export function initTooltips() {
17
+ // Delegate to body for all tooltip targets
18
+ delegate('body', {
19
+ target: '[data-tippy-content]', // Any element with data-tippy-content
20
+ allowHTML: true,
21
21
  theme: 'sds',
22
22
  placement: 'top',
23
23
  arrow: true,
24
24
  animation: 'shift-away',
25
25
  duration: [200, 150],
26
26
  maxWidth: 280,
27
- allowHTML: true, // Allow HTML content for rich tooltips
27
+ // Only show tooltip if there's actual content
28
+ onShow(instance) {
29
+ const content = instance.props.content;
30
+ if (!content || content === '' || content === 'undefined') {
31
+ return false;
32
+ }
33
+ },
28
34
  });
29
35
  }
30
36
 
31
- // Initialize on page load
32
- if (typeof window !== 'undefined') {
33
- // Initial load
34
- document.addEventListener('DOMContentLoaded', () => {
37
+ // Auto-initialize on Astro page load (for View Transitions)
38
+ if (typeof document !== 'undefined') {
39
+ document.addEventListener('astro:page-load', () => {
35
40
  initTooltips();
36
- // Reinitialize after a short delay to catch Vue components
37
- setTimeout(initTooltips, 100);
38
41
  });
39
42
 
40
- // Reinitialize on Astro page transitions (for View Transitions)
41
- document.addEventListener('astro:page-load', () => {
43
+ // Fallback for non-Astro or initial load
44
+ if (document.readyState === 'loading') {
45
+ document.addEventListener('DOMContentLoaded', () => {
46
+ initTooltips();
47
+ });
48
+ } else {
42
49
  initTooltips();
43
- // Reinitialize after a short delay to catch Vue components
44
- setTimeout(initTooltips, 100);
45
- });
50
+ }
46
51
  }
@@ -3,3 +3,7 @@
3
3
  @import 'base/grid';
4
4
 
5
5
  @import 'content.css';
6
+
7
+ /* Tippy.js for tooltips (used by ProductEngine component) */
8
+ @import 'tippy.js/dist/tippy.css';
9
+ @import './tippy-theme.css';
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Generates HTML content for engine tooltip
3
+ * Used by both SSR (Astro) and CSR (Tippy.js delegation)
4
+ */
5
+
6
+ export interface Engine {
7
+ id?: number;
8
+ code: string;
9
+ name?: string;
10
+ info?: string | null;
11
+ serie?: {
12
+ value: string;
13
+ label: string;
14
+ };
15
+ type?: {
16
+ value: string;
17
+ translated: string;
18
+ label: string;
19
+ };
20
+ power?: {
21
+ kw: number;
22
+ ps: number;
23
+ ps_label: string;
24
+ label: string;
25
+ };
26
+ date?: {
27
+ value: string;
28
+ label: string;
29
+ };
30
+ displacement?: {
31
+ value: number;
32
+ label: string;
33
+ };
34
+ compression_ratio?: {
35
+ value: string | null;
36
+ label: string;
37
+ };
38
+ valves?: {
39
+ value: number | null;
40
+ label: string;
41
+ };
42
+ euro?: {
43
+ value: number;
44
+ label: string;
45
+ };
46
+ pivot?: any;
47
+
48
+ // Backward compatibility - old flat structure
49
+ kw?: number;
50
+ ps?: number;
51
+ cc?: number;
52
+ c_ratio?: string | null;
53
+ }
54
+
55
+ export interface EngineTranslations {
56
+ power?: string;
57
+ cc?: string;
58
+ compressionRatio?: string;
59
+ valves?: string;
60
+ euro?: string;
61
+ horsepowerUnit?: string;
62
+ }
63
+
64
+ const defaultTranslations: EngineTranslations = {
65
+ power: 'Power',
66
+ cc: 'CC',
67
+ compressionRatio: 'C. Ratio',
68
+ valves: 'Valves',
69
+ euro: 'Euro',
70
+ horsepowerUnit: 'PS',
71
+ };
72
+
73
+ // Helper to get series value (supports both old and new API)
74
+ function getSerieValue(engine: Engine): string {
75
+ if (engine.serie && typeof engine.serie === 'object') {
76
+ return engine.serie.value;
77
+ }
78
+ // Backward compatibility - old API
79
+ const serie = engine.serie as any;
80
+ if (!serie) return '';
81
+ return serie === 3 ? 'EA288' : serie === 2 ? 'EA189' : `Serie ${serie}`;
82
+ }
83
+
84
+ export function getEngineTooltipContent(
85
+ engine: Engine,
86
+ translations: EngineTranslations = {}
87
+ ): string {
88
+ const t = { ...defaultTranslations, ...translations };
89
+
90
+ // Header section
91
+ let headerContent = `<strong>${engine.name || engine.code}</strong>`;
92
+ if (engine.info) {
93
+ headerContent += ` <span class="info">${engine.info}</span>`;
94
+ }
95
+ const serieValue = getSerieValue(engine);
96
+ if (serieValue) {
97
+ headerContent += `<div class="series-badge">${serieValue}</div>`;
98
+ }
99
+
100
+ const header = `<div class="tooltip-header">${headerContent}</div>`;
101
+
102
+ // Specs rows
103
+ const rows: string[] = [];
104
+
105
+ // Power (supports both new and old API structure)
106
+ const power = engine.power;
107
+ const oldKw = engine.kw;
108
+ const oldPs = engine.ps;
109
+
110
+ if (power || oldKw || oldPs) {
111
+ const powerValues: string[] = [];
112
+ const kw = power?.kw || oldKw;
113
+ const ps = power?.ps || oldPs;
114
+ const psLabel = power?.ps_label || t.horsepowerUnit;
115
+ const powerLabel = power?.label || t.power;
116
+
117
+ if (kw) powerValues.push(`${kw} kW`);
118
+ if (ps) powerValues.push(`${ps} ${psLabel}`);
119
+
120
+ if (powerValues.length) {
121
+ rows.push(
122
+ `<div class="tooltip-row"><span class="tooltip-label">${powerLabel}:</span><span class="tooltip-value">${powerValues.join(' / ')}</span></div>`
123
+ );
124
+ }
125
+ }
126
+
127
+ // Displacement (CC)
128
+ const displacement = engine.displacement;
129
+ const oldCc = engine.cc;
130
+
131
+ if (displacement || oldCc) {
132
+ const ccValue = displacement?.value || oldCc;
133
+ const ccLabel = displacement?.label || t.cc;
134
+
135
+ if (ccValue) {
136
+ rows.push(
137
+ `<div class="tooltip-row"><span class="tooltip-label">${ccLabel}:</span><span class="tooltip-value">${ccValue} cm³</span></div>`
138
+ );
139
+ }
140
+ }
141
+
142
+ // Euro standard
143
+ const euro = engine.euro;
144
+
145
+ if (euro && typeof euro === 'object') {
146
+ if (euro.value) {
147
+ rows.push(
148
+ `<div class="tooltip-row"><span class="tooltip-label">${euro.label}:</span><span class="tooltip-value">Euro ${euro.value}</span></div>`
149
+ );
150
+ }
151
+ } else if (euro) {
152
+ // Backward compatibility - old API
153
+ rows.push(
154
+ `<div class="tooltip-row"><span class="tooltip-label">${t.euro}:</span><span class="tooltip-value">Euro ${euro}</span></div>`
155
+ );
156
+ }
157
+
158
+ const specsContent = rows.length
159
+ ? `<div class="tooltip-specs">${rows.join('')}</div>`
160
+ : '';
161
+
162
+ return header + specsContent;
163
+ }
@@ -207,7 +207,7 @@ export function createSdsConfig(customConfig: CustomConfig = {}) {
207
207
  presetAttributify(),
208
208
  presetIcons({
209
209
  scale: 1.2,
210
- warn: true, // Show warnings for actual missing icons
210
+ warn: false, // Disabled to prevent false positives from JS code scanning
211
211
  prefix: 'i-',
212
212
  extraProperties: {
213
213
  'display': 'inline-block',