spoko-design-system 1.22.0 → 1.23.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.
package/.husky/pre-commit CHANGED
@@ -12,13 +12,14 @@ if git diff --cached --name-only | xargs grep -n "console\\.log" 2>/dev/null; th
12
12
  echo " Consider removing them before committing to production"
13
13
  fi
14
14
 
15
- # Run format check
16
- echo "🎨 Checking code formatting..."
17
- pnpm run format:check
18
- if [ $? -ne 0 ]; then
19
- echo "❌ Format check failed! Run 'pnpm run format' to fix."
20
- exit 1
21
- fi
15
+ # Auto-format code
16
+ echo "🎨 Auto-formatting code..."
17
+ pnpm run format
18
+
19
+ # Re-add formatted files to staging
20
+ git add -u
21
+
22
+ echo "✅ Code formatted successfully!"
22
23
 
23
24
  # Run linting
24
25
  echo "🔍 Running linter..."
@@ -1,22 +1,26 @@
1
1
  {
2
2
  "editor.formatOnSave": true,
3
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
3
4
  "editor.codeActionsOnSave": {
4
5
  "source.fixAll.eslint": "explicit"
5
6
  },
6
7
  "[vue]": {
7
8
  "editor.defaultFormatter": "esbenp.prettier-vscode"
8
9
  },
10
+ "[astro]": {
11
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
12
+ },
9
13
  "[typescript]": {
10
14
  "editor.defaultFormatter": "esbenp.prettier-vscode"
11
15
  },
12
16
  "[javascript]": {
13
17
  "editor.defaultFormatter": "esbenp.prettier-vscode"
14
18
  },
15
- "eslint.validate": [
16
- "javascript",
17
- "javascriptreact",
18
- "typescript",
19
- "typescriptreact",
20
- "vue"
21
- ]
19
+ "[json]": {
20
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
21
+ },
22
+ "[markdown]": {
23
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
24
+ },
25
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"]
22
26
  }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [1.23.1](https://github.com/polo-blue/sds/compare/v1.23.0...v1.23.1) (2025-12-15)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **header:** correct WPHeader microdata and improve semantics ([a37a26e](https://github.com/polo-blue/sds/commit/a37a26ef62a29e3e327076d715e2989102e4d60b))
6
+
7
+ ## [1.23.0](https://github.com/polo-blue/sds/compare/v1.22.0...v1.23.0) (2025-12-15)
8
+
9
+ ### Features
10
+
11
+ * **breadcrumbs:** add withMicrodata prop to control Schema.org rendering ([0ca52cc](https://github.com/polo-blue/sds/commit/0ca52cccb90a30f532cdb381e428480f59a6d433))
12
+
1
13
  ## [1.22.0](https://github.com/polo-blue/sds/compare/v1.21.1...v1.22.0) (2025-12-15)
2
14
 
3
15
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoko-design-system",
3
- "version": "1.22.0",
3
+ "version": "1.23.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./index.ts",
@@ -138,7 +138,7 @@
138
138
  "prettier-plugin-astro": "^0.14.1",
139
139
  "semantic-release": "^25.0.2",
140
140
  "unocss": "^0.65.0",
141
- "vite": "^7.2.7"
141
+ "vite": "^7.3.0"
142
142
  },
143
143
  "packageManager": "pnpm@10.17.1",
144
144
  "pnpm": {
@@ -5,6 +5,7 @@ export interface Breadcrumb {
5
5
  }
6
6
 
7
7
  import type { PropType } from 'vue';
8
+ import { computed } from 'vue';
8
9
 
9
10
  const props = defineProps({
10
11
  showBack: {
@@ -29,11 +30,36 @@ const props = defineProps({
29
30
  required: false,
30
31
  default: null,
31
32
  },
33
+ withMicrodata: {
34
+ type: Boolean,
35
+ required: false,
36
+ default: true,
37
+ },
32
38
  });
33
39
 
34
40
  const isLast = (index: number) => {
35
41
  return index === props.breadcrumbs.length - 1;
36
42
  };
43
+
44
+ // Microdata attributes - only added when withMicrodata is true
45
+ const listMicrodata = computed(() =>
46
+ props.withMicrodata
47
+ ? {
48
+ itemscope: true,
49
+ itemtype: 'https://schema.org/BreadcrumbList',
50
+ }
51
+ : {}
52
+ );
53
+
54
+ const listItemMicrodata = computed(() =>
55
+ props.withMicrodata
56
+ ? {
57
+ itemprop: 'itemListElement',
58
+ itemscope: true,
59
+ itemtype: 'https://schema.org/ListItem',
60
+ }
61
+ : {}
62
+ );
37
63
  </script>
38
64
 
39
65
  <template>
@@ -45,28 +71,22 @@ const isLast = (index: number) => {
45
71
  </button>
46
72
  </li>
47
73
  </ul>
48
- <ul
49
- class="breadcrumbs-base overflow-x-auto overflow-y-hidden sm:mr-12"
50
- itemscope
51
- itemtype="https://schema.org/BreadcrumbList"
52
- >
74
+ <ul class="breadcrumbs-base overflow-x-auto overflow-y-hidden sm:mr-12" v-bind="listMicrodata">
53
75
  <li v-if="props.showHome" class="breadcrumb-item">
54
76
  <a
55
77
  href="/"
56
78
  class="breadcrumb-link flex items-center px-3 sm:px-0 py-4.25 sm:py-1 hover:text-brand-secondary whitespace-nowrap translate-y-0 text-sm my-auto"
57
79
  :title="props.textBack"
58
- itemprop="item"
80
+ v-bind="withMicrodata ? { itemprop: 'item' } : {}"
59
81
  i-carbon-home
60
82
  />
61
- <meta itemprop="position" content="1" />
83
+ <meta v-if="withMicrodata" itemprop="position" content="1" />
62
84
  </li>
63
85
  <li
64
86
  v-for="(crumb, index) in breadcrumbs"
65
87
  :key="index"
66
88
  class="breadcrumb-item"
67
- itemprop="itemListElement"
68
- itemscope
69
- itemtype="https://schema.org/ListItem"
89
+ v-bind="listItemMicrodata"
70
90
  >
71
91
  <span v-if="index > 0 || props.showHome" class="text-gray-400 px-1 py-4.25 sm:py-1">/</span>
72
92
 
@@ -74,25 +94,31 @@ const isLast = (index: number) => {
74
94
  v-if="!isLast(index)"
75
95
  :href="crumb.path"
76
96
  class="breadcrumb-link"
77
- itemprop="item"
97
+ v-bind="withMicrodata ? { itemprop: 'item' } : {}"
78
98
  :title="`Polo 6R ${crumb.name}`"
79
99
  >
80
- <strong class="font-normal" itemprop="name">{{ crumb.name }}</strong>
100
+ <strong class="font-normal" v-bind="withMicrodata ? { itemprop: 'name' } : {}">
101
+ {{ crumb.name }}
102
+ </strong>
81
103
  </a>
82
104
  <a
83
105
  v-else
84
106
  :href="crumb.path"
85
107
  class="breadcrumb-link breadcrumb-link-disabled"
86
108
  :title="`Polo 6R ${crumb.name} ${productNumber}`"
87
- itemprop="item"
109
+ v-bind="withMicrodata ? { itemprop: 'item' } : {}"
88
110
  >
89
- <span class="font-normal" itemprop="name">
111
+ <span class="font-normal" v-bind="withMicrodata ? { itemprop: 'name' } : {}">
90
112
  <span v-html="crumb.name" />
91
113
  <b v-if="productNumber" class="hidden sm:inline font-normal ml-1">&nbsp;{{ productNumber }}</b>
92
114
  </span>
93
115
  </a>
94
116
 
95
- <meta itemprop="position" :content="String(props.showHome ? index + 2 : index + 1)" />
117
+ <meta
118
+ v-if="withMicrodata"
119
+ itemprop="position"
120
+ :content="String(props.showHome ? index + 2 : index + 1)"
121
+ />
96
122
  </li>
97
123
  </ul>
98
124
  </nav>
@@ -18,9 +18,9 @@ const navItemsLeft = [
18
18
  ];
19
19
  ---
20
20
 
21
- <nav
21
+ <header
22
22
  itemscope
23
- itemtype="http://schema.org/WPHeader"
23
+ itemtype="https://schema.org/WPHeader"
24
24
  class:list={[
25
25
  className,
26
26
  'nav print-hidden mx-auto px-4 shadow-md relative flex items-center justify-between h-24 sm:h-14 flex-wrap sm:flex-nowrap pt-1 sm:pt-0',
@@ -30,7 +30,12 @@ const navItemsLeft = [
30
30
  <slot name="logo" />
31
31
 
32
32
  <div class="hidden sm:block sm:ml-6">
33
- <div class="flex space-x-4" itemprop="hasPart">
33
+ <nav
34
+ class="flex space-x-4"
35
+ itemprop="hasPart"
36
+ itemscope
37
+ itemtype="https://schema.org/SiteNavigationElement"
38
+ >
34
39
  {
35
40
  navItemsLeft.map(({ title, description, url }) => (
36
41
  <a
@@ -43,7 +48,7 @@ const navItemsLeft = [
43
48
  </a>
44
49
  ))
45
50
  }
46
- </div>
51
+ </nav>
47
52
  </div>
48
53
  </div>
49
54
 
@@ -51,17 +56,17 @@ const navItemsLeft = [
51
56
  <slot name="search" />
52
57
  </div>
53
58
 
54
- <div
59
+ <nav
55
60
  class="flex items-center pr-0 sm:static sm:inset-auto ml-auto sm:ml-6 -mr-2 print:hidden order-3 sm:order-3 w-20 justify-end"
56
61
  itemprop="hasPart"
57
62
  itemscope
58
- itemtype="http://schema.org/SiteNavigationElement"
63
+ itemtype="https://schema.org/SiteNavigationElement"
59
64
  >
60
65
  <a class="icon-btn mx-2" title="" aria-label="" href="#" itemprop="url" data-astro-reload>
61
66
  <Icon name="carbon:language" />
62
67
  </a>
63
- </div>
64
- </nav>
68
+ </nav>
69
+ </header>
65
70
 
66
71
  <style is:global>
67
72
  nav {
@@ -102,6 +102,35 @@ https://schema.org/ListItem
102
102
  />
103
103
  ```
104
104
 
105
+ ## Props
106
+
107
+ | Prop | Type | Default | Description |
108
+ | --------------- | --------- | ------- | --------------------------------------------------------------------------- |
109
+ | `breadcrumbs` | Array | - | Array of breadcrumb objects with `name` and `path` properties (required) |
110
+ | `showBack` | Boolean | `false` | Show back button |
111
+ | `textBack` | String | `'Back'`| Text for back button title |
112
+ | `showHome` | Boolean | `false` | Show home icon as first breadcrumb |
113
+ | `productNumber` | String | `null` | Product number to display after last breadcrumb (mobile hidden) |
114
+ | `withMicrodata` | Boolean | `true` | Include Schema.org BreadcrumbList microdata (set to `false` for duplicates) |
115
+
116
+ ## Microdata Control
117
+
118
+ When rendering breadcrumbs multiple times on the same page (e.g., mobile and desktop versions), use `withMicrodata` to prevent duplicate Schema.org markup:
119
+
120
+ ```js
121
+ <!-- Desktop version - WITH microdata (default) -->
122
+ <Breadcrumbs
123
+ breadcrumbs={[...]}
124
+ class="hidden md:block"
125
+ />
126
+
127
+ <!-- Mobile version - WITHOUT microdata -->
128
+ <Breadcrumbs
129
+ breadcrumbs={[...]}
130
+ class="block md:hidden"
131
+ :withMicrodata="false"
132
+ />
133
+ ```
105
134
 
106
135
  ### Schema support:
107
136
  - https://developers.google.com/search/docs/appearance/structured-data/breadcrumb?hl=en