spoko-design-system 1.28.0 → 1.28.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [1.28.2](https://github.com/polo-blue/sds/compare/v1.28.1...v1.28.2) (2026-03-08)
2
+
3
+ ### ⚠ BREAKING CHANGES
4
+
5
+ * ButtonCopy, CategoryDetails, and LanguageSuggestion
6
+ are no longer available from the barrel import. Use direct subpath
7
+ imports instead:
8
+ import ButtonCopy from 'spoko-design-system/components/ButtonCopy.astro'
9
+ import CategoryDetails from 'spoko-design-system/components/Category/CategoryDetails.astro'
10
+ import LanguageSuggestion from 'spoko-design-system/components/LanguageSuggestion.astro'
11
+
12
+ ### Bug Fixes
13
+
14
+ * remove Astro components with <script> tags from barrel file ([36f36c2](https://github.com/polo-blue/sds/commit/36f36c2316aeb0a6248f4fe62450d730e9f9dac8))
15
+
16
+ ## [1.28.1](https://github.com/polo-blue/sds/compare/v1.28.0...v1.28.1) (2026-03-07)
17
+
18
+ ### Bug Fixes
19
+
20
+ * **homepage:** reduce CLS with font preloading and layout stabilization ([32d08f5](https://github.com/polo-blue/sds/commit/32d08f5b84c8b17f209073eaab7e6030f9dc53f9))
21
+
1
22
  ## [1.28.0](https://github.com/polo-blue/sds/compare/v1.27.3...v1.28.0) (2026-03-04)
2
23
 
3
24
  ### Features
package/CLAUDE.md CHANGED
@@ -52,6 +52,26 @@ export { default as Button } from './src/components/Button.vue';
52
52
  export { default as Button } from './components/Button.vue';
53
53
  ```
54
54
 
55
+ ### Astro Components with `<script>` Tags — Barrel File Restriction
56
+
57
+ Astro components with `<script>` tags **MUST NOT** be exported from the barrel file (`index.ts`).
58
+ When any consumer imports from the barrel, Astro hoists ALL `<script>` tags from ALL exported `.astro`
59
+ components onto every page — even if the component is never rendered. This is a
60
+ [known Astro limitation](https://github.com/withastro/astro/issues/6906).
61
+
62
+ **Rule:** If an `.astro` component has a `<script>` tag, export it ONLY via subpath (`./components/*`):
63
+ ```typescript
64
+ // ❌ DO NOT add to index.ts — script will load on ALL consumer pages
65
+ export { default as ButtonCopy } from './src/components/ButtonCopy.astro';
66
+
67
+ // ✅ Consumer uses direct subpath import instead
68
+ import ButtonCopy from 'spoko-design-system/components/ButtonCopy.astro';
69
+ ```
70
+
71
+ Components **without** `<script>` tags are safe in the barrel file.
72
+
73
+ Currently excluded from barrel: **ButtonCopy**, **CategoryDetails**, **LanguageSuggestion**.
74
+
55
75
  ### UnoCSS Configuration System
56
76
 
57
77
  **Critical**: UnoCSS config is split across two locations:
@@ -198,10 +218,12 @@ On merge to `main`, GitHub Actions automatically:
198
218
  - Vue: `src/components/ComponentName.vue`
199
219
  - Astro: `src/components/ComponentName.astro`
200
220
 
201
- 2. Export from root `index.ts`:
221
+ 2. Export from root `index.ts` (Vue components and Astro components **without** `<script>` tags):
202
222
  ```typescript
203
223
  export { default as ComponentName } from './src/components/ComponentName.vue';
204
224
  ```
225
+ **Important:** If the Astro component has a `<script>` tag, do **NOT** add it to `index.ts`.
226
+ It will be available via the wildcard subpath export `./components/*` automatically.
205
227
 
206
228
  3. Add documentation page in `src/pages/components/component-name.mdx`
207
229
 
package/index.ts CHANGED
@@ -9,7 +9,6 @@ export { default as FlagPL } from './src/components/flags/FlagPL.vue';
9
9
  export { default as Badges } from './src/components/Badges.vue';
10
10
  export { default as SlimBanner } from './src/components/SlimBanner.vue';
11
11
 
12
- export { default as Jumbotron } from './src/components/Jumbotron.astro';
13
12
  export { default as Button } from './src/components/Button.vue';
14
13
  export { default as Breadcrumbs } from './src/components/Breadcrumbs.vue';
15
14
  export { default as ProductDetailsList } from './src/components/ProductDetailsList.vue';
@@ -29,10 +28,7 @@ export { default as ProductModel } from './src/components/Product/ProductModel.v
29
28
  export { default as ProductModels } from './src/components/Product/ProductModels.vue';
30
29
  export { default as ProductName } from './src/components/Product/ProductName.vue';
31
30
  export { default as ProductPositions } from './src/components/Product/ProductPositions.vue';
32
- export { default as ProductNumber } from './src/components/Product/ProductNumber.astro';
33
31
  export { default as ProductLink } from './src/components/Product/ProductLink.vue';
34
- // export { default as ProductCarousel } from './src/components/Product/ProductCarousel.astro';
35
- export { default as LanguageSuggestion } from './src/components/LanguageSuggestion.astro';
36
32
  export { default as Input } from './src/components/Input.vue';
37
33
 
38
34
  export { default as PostCategories } from './src/components/Post/PostCategories.vue';
@@ -40,21 +36,24 @@ export { default as CategorySidebarToggler } from './src/components/Category/Cat
40
36
  export { default as SubCategoryLink } from './src/components/Category/SubCategoryLink.vue';
41
37
  export { default as CategoryLink } from './src/components/Category/CategoryLink.vue';
42
38
 
43
- // Astro Components
44
- export { default as Copyright } from './src/components/Copyright.astro';
39
+ // Astro Components (without <script> tags — safe in barrel)
40
+ export { default as Jumbotron } from './src/components/Jumbotron.astro';
41
+ export { default as Copyright } from './src/components/Copyright.astro';
45
42
  export { default as HandDrive } from './src/components/HandDrive.astro';
46
43
  export { default as Faq } from './src/components/Faq.astro';
47
44
  export { default as FaqItem } from './src/components/FaqItem.astro';
48
- export { default as ButtonCopy } from './src/components/ButtonCopy.astro';
49
45
  export { default as CallToAction } from './src/components/layout/CallToAction.astro';
50
-
46
+ export { default as ProductNumber } from './src/components/Product/ProductNumber.astro';
51
47
  export { default as ProductImage } from './src/components/Product/ProductImage.astro';
52
48
  export { default as ProductEngine } from './src/components/Product/ProductEngine.astro';
53
49
  export { default as ProductEngines } from './src/components/Product/ProductEngines.astro';
54
-
55
- export { default as CategoryDetails } from './src/components/Category/CategoryDetails.astro';
56
50
  export { default as CategoryTile } from './src/components/Category/CategoryTile.astro';
57
51
 
52
+ // Astro Components WITH <script> tags — NOT in barrel to prevent script hoisting.
53
+ // Import directly: import ButtonCopy from 'spoko-design-system/components/ButtonCopy.astro'
54
+ // See: https://github.com/withastro/astro/issues/6906
55
+ // - ButtonCopy, CategoryDetails, LanguageSuggestion
56
+
58
57
 
59
58
  // Utils: Product
60
59
  export { default as getPriceFormatted } from './src/utils/product/getPriceFormatted';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoko-design-system",
3
- "version": "1.28.0",
3
+ "version": "1.28.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./index.ts",
@@ -15,7 +15,8 @@
15
15
  "./scripts/*": "./src/scripts/*",
16
16
  "./icons": "./icon.config.ts",
17
17
  "./icon-collections": "./icon.collections.ts",
18
- "./uno-config": "./uno-config/index.ts"
18
+ "./uno-config": "./uno-config/index.ts",
19
+ "./components/*": "./src/components/*"
19
20
  },
20
21
  "scripts": {
21
22
  "dev": "astro dev",
@@ -13,9 +13,8 @@ import { pwaInfo } from 'virtual:pwa-info';
13
13
  <link rel="sitemap" href="/sitemap-index.xml" />
14
14
 
15
15
  <!-- Preload Fonts -->
16
- <!-- <link rel="preconnect" href="https://fonts.googleapis.com" />
17
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
18
- <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet" /> -->
16
+ <link rel="preload" href="/fonts/vwhead-light-webfont.woff2" as="font" type="font/woff2" crossorigin />
17
+ <link rel="preload" href="/fonts/vwhead-regular-webfont.woff2" as="font" type="font/woff2" crossorigin />
19
18
 
20
19
  <!-- Scrollable a11y code helper -->
21
20
  <script src="/make-scrollable-code-focusable.js" is:inline></script>
@@ -95,7 +95,7 @@ const exampleSites = [
95
95
  <Jumbotron title={SITE.title}>
96
96
  <p
97
97
  slot="subtitle"
98
- class="mt-3 text-base text-gray-200 sm:mt-5 text-lg md:text-xl lg:mx-0 md:mt-5"
98
+ class="mt-3 text-base text-gray-200 sm:mt-5 text-lg md:text-xl lg:mx-0 md:mt-5 leading-snug"
99
99
  set:html={SITE.description}
100
100
  />
101
101
 
@@ -112,10 +112,10 @@ const exampleSites = [
112
112
  <a
113
113
  href={url}
114
114
  title={description}
115
- class="flex w-full flex-wrap bg-white rounded-md hover:-translate-y-1 hover:shadow-lg transition-all flex-1 items-center py-10 px-4 md:(flex-col w-auto flex-nowrap text-center py-16)"
115
+ class="flex w-full flex-wrap bg-white rounded-md hover:-translate-y-1 hover:shadow-lg transition-all flex-1 items-center py-10 px-4 min-h-24 md:(flex-col w-auto flex-nowrap text-center py-16 min-h-48)"
116
116
  >
117
117
  <Headline as="h2" class="text-3xl" underline={false}>
118
- <Icon name={icon} aria-hidden="true" class="text-blue-400 mr-3 text-4xl" />
118
+ <Icon name={icon} aria-hidden="true" class="text-blue-400 mr-3 text-4xl w-10 h-10 shrink-0" />
119
119
  {title}
120
120
  </Headline>
121
121
  <p class="text-slate-500 w-full">{description}</p>
@@ -140,13 +140,13 @@ const exampleSites = [
140
140
  <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
141
141
  {
142
142
  features.map(({ title, icon, description }) => (
143
- <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100">
143
+ <div class="text-center p-6 bg-white rounded-lg shadow-sm border border-gray-100 min-h-40">
144
144
  <Headline
145
145
  as="h3"
146
146
  class="text-2xl mb-3 text-gray-900 flex items-center justify-center"
147
147
  underline={false}
148
148
  >
149
- <Icon name={icon} aria-hidden="true" class="text-4xl text-blue-400 mr-3" />
149
+ <Icon name={icon} aria-hidden="true" class="text-4xl text-blue-400 mr-3 w-10 h-10 shrink-0" />
150
150
  {title}
151
151
  </Headline>
152
152
  <p class="text-gray-600" set:html={description} />
@@ -64,6 +64,25 @@
64
64
  font-display: swap;
65
65
  }
66
66
 
67
+ /* Fallback font metrics matching to reduce CLS on font swap */
68
+ @font-face {
69
+ font-family: 'vw_headlight_fallback';
70
+ src: local('Segoe UI'), local('Roboto'), local('Helvetica Neue'), local('Arial');
71
+ ascent-override: 78.7%;
72
+ descent-override: 21.3%;
73
+ line-gap-override: 0%;
74
+ size-adjust: 105%;
75
+ }
76
+
77
+ @font-face {
78
+ font-family: 'vw_headregular_fallback';
79
+ src: local('Segoe UI'), local('Roboto'), local('Helvetica Neue'), local('Arial');
80
+ ascent-override: 78.7%;
81
+ descent-override: 21.3%;
82
+ line-gap-override: 21.9%;
83
+ size-adjust: 105%;
84
+ }
85
+
67
86
  /* for some content which don't need to index in Google */
68
87
  [data-text]:before {
69
88
  content: attr(data-text);
@@ -6,12 +6,12 @@ export const jumbotronShortcuts = [
6
6
  // Default variant
7
7
  ['jumbotron-header-base', 'relative mx-auto my-auto w-full text-center py-8'],
8
8
  ['jumbotron-container-small', 'md:min-h-xs sm:py-12 md:py-14 lg:py-16 xl:py-20'],
9
- ['jumbotron-container-large', 'md:min-h-md sm:py-16 md:py-20 lg:py-28 xl:py-32'],
9
+ ['jumbotron-container-large', 'min-h-48 md:min-h-md sm:py-16 md:py-20 lg:py-28 xl:py-32'],
10
10
  [
11
11
  'jumbotron-title-default',
12
- 'text-3xl headline-light text-white sm:(text-4xl pt-0) md:text-5xl lg:text-6xl',
12
+ 'text-3xl headline-light text-white sm:(text-4xl pt-0) md:text-5xl lg:text-6xl leading-[1.15]',
13
13
  ],
14
- ['jumbotron-cta-wrapper', 'mt-5 sm:(mt-8 flex justify-center)'],
14
+ ['jumbotron-cta-wrapper', 'mt-5 min-h-12 sm:(mt-8 flex justify-center)'],
15
15
 
16
16
  // Hero variant
17
17
  ['jumbotron-hero-wrapper', 'relative w-full'],
@@ -17,8 +17,8 @@ interface FontFamily {
17
17
  sans: ['vw_textregular', 'system-ui', 'ui-sans-serif'],
18
18
  novamono: ['Nova Mono'],
19
19
  mono: ['Nova Mono'],
20
- headlight: ['vw_headlight', 'system-ui'],
21
- headregular: ['vw_headregular'],
20
+ headlight: ['vw_headlight', 'vw_headlight_fallback', 'system-ui'],
21
+ headregular: ['vw_headregular', 'vw_headregular_fallback', 'system-ui'],
22
22
  headbold: ['vw_headbold'],
23
23
  textlight: ['vw_textlight'],
24
24
  textregular: ['vw_textregular'],