@santi020k/theme 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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/assets/banners/banner-1.png +0 -0
  4. package/assets/banners/banner-1.webp +0 -0
  5. package/assets/banners/banner-2.png +0 -0
  6. package/assets/banners/banner-2.webp +0 -0
  7. package/assets/banners/banner-3.png +0 -0
  8. package/assets/banners/banner-3.webp +0 -0
  9. package/assets/banners/banner-4.png +0 -0
  10. package/assets/banners/banner-4.webp +0 -0
  11. package/assets/banners/banner-5.png +0 -0
  12. package/assets/banners/banner-5.webp +0 -0
  13. package/assets/chrome/icons/icon-light.svg +5 -0
  14. package/assets/chrome/icons/icon-light128.png +0 -0
  15. package/assets/chrome/icons/icon-light16.png +0 -0
  16. package/assets/chrome/icons/icon-light48.png +0 -0
  17. package/assets/chrome/icons/icon-light512.png +0 -0
  18. package/assets/chrome/icons/icon.svg +6 -0
  19. package/assets/chrome/icons/icon128.png +0 -0
  20. package/assets/chrome/icons/icon16.png +0 -0
  21. package/assets/chrome/icons/icon48.png +0 -0
  22. package/assets/chrome/icons/icon512.png +0 -0
  23. package/assets/chrome/images/adaptive_assets_diagonal.webp +0 -0
  24. package/assets/chrome/images/adaptive_background.svg +12 -0
  25. package/assets/chrome/images/dark.png +0 -0
  26. package/assets/chrome/images/light.png +0 -0
  27. package/assets/chrome/images/theme_ntp_background.png +0 -0
  28. package/assets/chrome/images/theme_ntp_background.svg +71 -0
  29. package/assets/chrome/images/theme_ntp_background.webp +0 -0
  30. package/assets/chrome/images/theme_ntp_background_light.png +0 -0
  31. package/assets/chrome/images/theme_ntp_background_light.svg +71 -0
  32. package/assets/chrome/images/theme_ntp_background_light.webp +0 -0
  33. package/assets/chrome/images/wallpaper.heic +0 -0
  34. package/assets/chrome/store/marquee-banner-light.png +0 -0
  35. package/assets/chrome/store/marquee-banner.png +0 -0
  36. package/assets/chrome/store/promo-tile-light.png +0 -0
  37. package/assets/chrome/store/promo-tile.png +0 -0
  38. package/assets/chrome/store/screenshot-incognito-light.png +0 -0
  39. package/assets/chrome/store/screenshot-incognito.jpg +0 -0
  40. package/assets/chrome/store/screenshot-incognito.png +0 -0
  41. package/assets/chrome/store/screenshot-main-light.png +0 -0
  42. package/assets/chrome/store/screenshot-main.png +0 -0
  43. package/assets/chrome/store/screenshot-ntp-light.png +0 -0
  44. package/assets/chrome/store/screenshot-ntp.png +0 -0
  45. package/assets/favicons/apple-touch-icon.png +0 -0
  46. package/assets/favicons/apple-touch-icon.webp +0 -0
  47. package/assets/favicons/favicon-16x16.png +0 -0
  48. package/assets/favicons/favicon-16x16.webp +0 -0
  49. package/assets/favicons/favicon-32x32.png +0 -0
  50. package/assets/favicons/favicon-32x32.webp +0 -0
  51. package/assets/favicons/favicon.svg +6 -0
  52. package/assets/favicons/icon-192.png +0 -0
  53. package/assets/favicons/icon-192.webp +0 -0
  54. package/assets/favicons/icon-512.png +0 -0
  55. package/assets/favicons/icon-512.webp +0 -0
  56. package/assets/logos/logo-santi020k-dark.png +0 -0
  57. package/assets/logos/logo-santi020k-dark.svg +9 -0
  58. package/assets/logos/logo-santi020k-dark.webp +0 -0
  59. package/assets/logos/logo-santi020k.png +0 -0
  60. package/assets/logos/logo-santi020k.svg +9 -0
  61. package/assets/logos/logo-santi020k.webp +0 -0
  62. package/assets/logos/logo-square.png +0 -0
  63. package/assets/logos/logo-square.svg +6 -0
  64. package/assets/logos/logo-square.webp +0 -0
  65. package/assets/projects/santi020k-theme/cover-horizontal.webp +0 -0
  66. package/assets/projects/santi020k-theme/cover-v2.webp +0 -0
  67. package/assets/projects/santi020k-theme/cover-vertical.webp +0 -0
  68. package/assets/projects/santi020k-theme/cover.webp +0 -0
  69. package/assets/projects/santi020k-theme/logo.webp +0 -0
  70. package/assets/projects/santi020k-theme/preview-light.webp +0 -0
  71. package/assets/vscode/icon.png +0 -0
  72. package/assets/vscode/icon.svg +5 -0
  73. package/assets/vscode/previews/preview-dark.png +0 -0
  74. package/assets/vscode/previews/preview-dark.svg +63 -0
  75. package/assets/vscode/previews/preview-hc-dark.png +0 -0
  76. package/assets/vscode/previews/preview-hc-light.png +0 -0
  77. package/assets/vscode/previews/preview-light.png +0 -0
  78. package/assets/vscode/previews/preview-light.svg +63 -0
  79. package/assets/wallpapers/wallpaper-1-desktop.png +0 -0
  80. package/assets/wallpapers/wallpaper-1-desktop.webp +0 -0
  81. package/assets/wallpapers/wallpaper-1-mobile.png +0 -0
  82. package/assets/wallpapers/wallpaper-1-mobile.webp +0 -0
  83. package/assets/wallpapers/wallpaper-2-desktop.png +0 -0
  84. package/assets/wallpapers/wallpaper-2-desktop.webp +0 -0
  85. package/assets/wallpapers/wallpaper-2-mobile.png +0 -0
  86. package/assets/wallpapers/wallpaper-2-mobile.webp +0 -0
  87. package/assets/wallpapers/wallpaper-3-desktop.png +0 -0
  88. package/assets/wallpapers/wallpaper-3-desktop.webp +0 -0
  89. package/assets/wallpapers/wallpaper-3-mobile.png +0 -0
  90. package/assets/wallpapers/wallpaper-3-mobile.webp +0 -0
  91. package/assets/wallpapers/wallpaper.png +0 -0
  92. package/assets/wallpapers/wallpaper.webp +0 -0
  93. package/docs/brand-guidelines.md +7 -0
  94. package/index.d.ts +100 -0
  95. package/index.js +531 -0
  96. package/package.json +75 -0
  97. package/site.css +84 -0
  98. package/site.d.ts +27 -0
  99. package/site.js +22 -0
  100. package/tailwind.d.ts +24 -0
  101. package/tailwind.js +24 -0
  102. package/tokens/tokens.css +88 -0
  103. package/tokens/tokens.json +120 -0
  104. package/typography.css +9 -0
package/index.js ADDED
@@ -0,0 +1,531 @@
1
+ import {
2
+ getAssetByPath,
3
+ getAssetsByCategory,
4
+ getAssetsBySurface
5
+ } from '@santi020k/theme-core'
6
+
7
+ export const packageName = '@santi020k/theme'
8
+
9
+ export const chromeThemeVariants = ['dark', 'light']
10
+
11
+ export const chromeThemeVariantManifests = {
12
+ dark: {
13
+ manifest: 'manifest.json',
14
+ output: 'santi020k-chrome-theme.zip'
15
+ },
16
+ light: {
17
+ manifest: 'manifest-light.json',
18
+ output: 'santi020k-chrome-theme-light.zip'
19
+ }
20
+ }
21
+
22
+ export const chromeThemeImageRequirements = {
23
+ theme_ntp_background: {
24
+ format: 'png',
25
+ minWidth: 3840,
26
+ minHeight: 2160
27
+ }
28
+ }
29
+
30
+ export const chromeThemeSourceTokenRoles = [
31
+ { chrome: 'bookmark_text', source: 'editor.foreground' },
32
+ { chrome: 'button_background', source: 'sideBar.background', transform: 'buttonSurface' },
33
+ { chrome: 'control_background', source: 'tab.border' },
34
+ { chrome: 'frame', source: 'titleBar.activeBackground' },
35
+ { chrome: 'frame_inactive', source: 'titleBar.activeBackground', transform: 'inactiveFrame' },
36
+ { chrome: 'frame_incognito', source: 'titleBar.activeBackground', transform: 'incognitoFrame' },
37
+ { chrome: 'frame_incognito_inactive', source: 'titleBar.activeBackground', transform: 'inactiveIncognitoFrame' },
38
+ { chrome: 'background_tab', source: 'tab.inactiveBackground' },
39
+ { chrome: 'ntp_background', source: 'editor.background' },
40
+ { chrome: 'ntp_header', source: 'sideBar.background' },
41
+ { chrome: 'ntp_link', source: 'textLink.foreground' },
42
+ { chrome: 'ntp_link_underline', source: 'textLink.foreground' },
43
+ { chrome: 'ntp_section', source: 'sideBar.background' },
44
+ { chrome: 'ntp_section_link', source: 'textLink.activeForeground' },
45
+ { chrome: 'ntp_section_text', source: 'icon.foreground' },
46
+ { chrome: 'ntp_text', source: 'editor.foreground' },
47
+ { chrome: 'omnibox_background', source: 'editor.background' },
48
+ { chrome: 'omnibox_text', source: 'editor.foreground' },
49
+ { chrome: 'tab_background_separator', source: 'tab.border' },
50
+ { chrome: 'tab_background_text', source: 'chrome.variant.inactiveTabText' },
51
+ { chrome: 'tab_background_text_inactive', source: 'tab.inactiveForeground' },
52
+ { chrome: 'tab_background_text_incognito', source: 'tab.inactiveForeground' },
53
+ { chrome: 'tab_background_text_incognito_inactive', source: 'tab.unfocusedInactiveForeground' },
54
+ { chrome: 'tab_line', source: 'tab.activeBorder' },
55
+ { chrome: 'tab_text', source: 'tab.activeForeground' },
56
+ { chrome: 'toolbar', source: 'sideBar.background' },
57
+ { chrome: 'toolbar_button_icon', source: 'icon.foreground' },
58
+ { chrome: 'toolbar_text', source: 'editor.foreground' }
59
+ ]
60
+
61
+ export const chromeThemeContrastPairs = [
62
+ { fg: 'tab_text', bg: 'frame', label: 'Active Tab Text' },
63
+ { fg: 'tab_background_text', bg: 'background_tab', label: 'Inactive Tab Text' },
64
+ { fg: 'tab_background_text_inactive', bg: 'background_tab', label: 'Unfocused Tab Text', minRatio: 3 },
65
+ { fg: 'tab_background_text_incognito', bg: 'frame_incognito', label: 'Incognito Tab Text', minRatio: 3 },
66
+ { fg: 'tab_background_text_incognito_inactive', bg: 'frame_incognito_inactive', label: 'Incognito Inactive Tab Text', minRatio: 3 },
67
+ { fg: 'tab_line', bg: 'frame', label: 'Active Tab Accent', minRatio: 3 },
68
+ { fg: 'bookmark_text', bg: 'toolbar', label: 'Bookmark Text' },
69
+ { fg: 'ntp_text', bg: 'ntp_background', label: 'NTP Text' },
70
+ { fg: 'ntp_link', bg: 'ntp_background', label: 'NTP Link' },
71
+ { fg: 'ntp_section_text', bg: 'ntp_section', label: 'NTP Section Text' },
72
+ { fg: 'ntp_section_link', bg: 'ntp_section', label: 'NTP Section Link' },
73
+ { fg: 'omnibox_text', bg: 'omnibox_background', label: 'Omnibox Text' },
74
+ { fg: 'toolbar_text', bg: 'toolbar', label: 'Toolbar Text' },
75
+ { fg: 'toolbar_button_icon', bg: 'toolbar', label: 'Toolbar Icons', minRatio: 3 }
76
+ ]
77
+
78
+ export const chromeRuntimeAssetEntries = [
79
+ { destination: 'icons', source: 'assets/chrome/icons' },
80
+ { destination: 'images', source: 'assets/chrome/images' }
81
+ ]
82
+
83
+ export const isChromeThemeVariant = variant => chromeThemeVariants.includes(variant)
84
+
85
+ export const hexToRgb = hex => {
86
+ const value = hex.slice(0, 7)
87
+ const normalized = value.replace('#', '')
88
+ const parsed = Number.parseInt(normalized, 16)
89
+
90
+ return [parsed >> 16 & 0xff, parsed >> 8 & 0xff, parsed & 0xff]
91
+ }
92
+
93
+ export const darkenRgb = (rgb, factor) => rgb.map(value => Math.round(value * factor))
94
+
95
+ export const darkenHex = (hex, factor) => darkenRgb(hexToRgb(hex), factor)
96
+
97
+ export const clampRgb = rgb => rgb.map(value => Math.max(0, Math.min(255, value)))
98
+
99
+ export const getRgbLuminance = rgb => {
100
+ const [r, g, b] = rgb.map(value => {
101
+ const s = value / 255
102
+
103
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4
104
+ })
105
+
106
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
107
+ }
108
+
109
+ export const getRgbContrastRatio = (rgb1, rgb2) => {
110
+ const l1 = getRgbLuminance(rgb1)
111
+ const l2 = getRgbLuminance(rgb2)
112
+
113
+ return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05)
114
+ }
115
+
116
+ export const createChromeThemeFromVSCodeColors = (vscodeColors, variant = 'dark') => {
117
+ if (!isChromeThemeVariant(variant)) {
118
+ throw new Error(`Invalid Chrome theme variant: ${variant}`)
119
+ }
120
+
121
+ const vscodeColorMap = new Map(Object.entries(vscodeColors))
122
+
123
+ const hex = (token, fallback) => {
124
+ const value = vscodeColorMap.get(token) || fallback
125
+
126
+ if (!value) throw new Error(`Missing VS Code token: ${token}`)
127
+
128
+ return value.slice(0, 7)
129
+ }
130
+
131
+ const frame = hex('titleBar.activeBackground')
132
+ const toolbar = hex('sideBar.background')
133
+ const editorBg = hex('editor.background')
134
+ const editorFg = hex('editor.foreground')
135
+ const iconFg = hex('icon.foreground')
136
+ const link = hex('textLink.foreground')
137
+ const linkActive = hex('textLink.activeForeground')
138
+ const tabBorder = hex('tab.border')
139
+ const tabInactiveBg = hex('tab.inactiveBackground')
140
+ const tabActiveFg = hex('tab.activeForeground')
141
+ const tabInactiveFg = hex('tab.inactiveForeground')
142
+ const tabAccent = hex('tab.activeBorder')
143
+ const unfocusedFg = hex('tab.unfocusedInactiveForeground')
144
+ const tabBackgroundText = variant === 'light' ? '#604c8a' : '#a19da8'
145
+
146
+ const colors = {
147
+ bookmark_text: hexToRgb(editorFg),
148
+ button_background: darkenHex(toolbar, variant === 'dark' ? 1.25 : 0.95),
149
+ control_background: hexToRgb(tabBorder),
150
+
151
+ frame: hexToRgb(frame),
152
+ frame_inactive: darkenHex(frame, 0.85),
153
+ frame_incognito: variant === 'dark' ? darkenHex(frame, 0.72) : [35, 29, 48],
154
+ frame_incognito_inactive: variant === 'dark' ? darkenHex(frame, 0.2) : [28, 21, 40],
155
+
156
+ background_tab: hexToRgb(tabInactiveBg),
157
+
158
+ ntp_background: hexToRgb(editorBg),
159
+ ntp_header: hexToRgb(toolbar),
160
+ ntp_link: hexToRgb(link),
161
+ ntp_link_underline: hexToRgb(link),
162
+ ntp_section: hexToRgb(toolbar),
163
+ ntp_section_link: hexToRgb(linkActive),
164
+ ntp_section_text: hexToRgb(iconFg),
165
+ ntp_text: hexToRgb(editorFg),
166
+
167
+ omnibox_background: hexToRgb(editorBg),
168
+ omnibox_text: hexToRgb(editorFg),
169
+
170
+ tab_background_separator: hexToRgb(tabBorder),
171
+ tab_background_text: hexToRgb(tabBackgroundText),
172
+ tab_background_text_inactive: hexToRgb(tabInactiveFg),
173
+ tab_background_text_incognito: hexToRgb(tabInactiveFg),
174
+ tab_background_text_incognito_inactive: hexToRgb(unfocusedFg),
175
+ tab_line: hexToRgb(tabAccent),
176
+ tab_text: hexToRgb(tabActiveFg),
177
+
178
+ toolbar: hexToRgb(toolbar),
179
+ toolbar_button_icon: hexToRgb(iconFg),
180
+ toolbar_text: hexToRgb(editorFg)
181
+ }
182
+
183
+ const properties = {
184
+ ntp_background_alignment: 'center',
185
+ ntp_background_repeat: 'no-repeat',
186
+ ntp_logo_alternate: variant === 'dark' ? 1 : 0
187
+ }
188
+
189
+ return {
190
+ colors: Object.fromEntries(
191
+ Object.entries(colors).map(([key, rgb]) => [key, clampRgb(rgb)])
192
+ ),
193
+ properties
194
+ }
195
+ }
196
+
197
+ export const colors = [
198
+ { name: 'theme-bg', light: '268 20% 98%', dark: '260 43% 8%', lightContrast: '268 20% 98%', darkContrast: '260 43% 8%', description: 'Page background' },
199
+ { name: 'surface', light: '268 20% 100%', dark: '260 30% 12%', description: 'Card and panel surfaces' },
200
+ { name: 'surface-muted', light: '268 20% 96%', dark: '260 25% 15%', lightContrast: '268 18% 94%', darkContrast: '260 25% 15%', description: 'Muted surfaces' },
201
+ { name: 'surface-strong', light: '268 15% 90%', dark: '260 20% 21%', lightContrast: '268 14% 88%', darkContrast: '260 20% 20%', description: 'Strong surfaces' },
202
+ { name: 'line', light: '268 15% 84%', dark: '260 15% 30%', lightContrast: '268 18% 66%', darkContrast: '260 15% 48%', description: 'Borders and dividers' },
203
+ { name: 'ink', light: '268 10% 20%', dark: '260 10% 88%', description: 'Primary text and headings' },
204
+ { name: 'ink-soft', light: '268 8% 36%', dark: '260 8% 72%', lightContrast: '268 12% 24%', darkContrast: '260 10% 84%', description: 'Body and secondary text' },
205
+ { name: 'ink-muted', light: '268 6% 28%', dark: '260 6% 56%', lightContrast: '268 10% 18%', darkContrast: '260 8% 76%', description: 'Muted labels and captions' },
206
+ { name: 'brand', light: '264 92% 47%', dark: '264 90% 58%', lightContrast: '264 92% 40%', darkContrast: '264 90% 62%', description: 'Primary brand purple' },
207
+ { name: 'brand-solid', light: '264 92% 42%', dark: '264 90% 52%', lightContrast: '264 92% 38%', darkContrast: '264 90% 55%', description: 'Saturated fills for accessible controls' },
208
+ { name: 'brand-soft', light: '264 60% 94%', dark: '264 45% 18%', lightContrast: '264 60% 92%', darkContrast: '264 45% 22%', description: 'Tinted surfaces and selections' },
209
+ { name: 'accent', light: '264 95% 57%', dark: '264 90% 68%', description: 'Hovers and active states' },
210
+ { name: 'glow', light: '264 95% 70%', dark: '264 85% 50%', description: 'Background gradients and highlights' }
211
+ ]
212
+
213
+ export const typography = {
214
+ display: '"Montserrat", "Avenir Next", sans-serif',
215
+ body: '"Montserrat", "Avenir Next", "Segoe UI", sans-serif',
216
+ mono: '"Montserrat", "SFMono-Regular", "SF Mono", monospace',
217
+ source: 'google-fonts',
218
+ importUrl: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap',
219
+ weights: [100, 200, 300, 400, 500, 600, 700, 800, 900]
220
+ }
221
+
222
+ export const fontFamily = {
223
+ sans: typography.body,
224
+ display: typography.display,
225
+ mono: typography.mono
226
+ }
227
+
228
+ export const staticAssets = {
229
+ 'favicon.svg': 'assets/favicons/favicon.svg',
230
+ 'favicon-16x16.png': 'assets/favicons/favicon-16x16.png',
231
+ 'favicon-32x32.png': 'assets/favicons/favicon-32x32.png',
232
+ 'apple-touch-icon.png': 'assets/favicons/apple-touch-icon.png',
233
+ 'apple-touch-icon.webp': 'assets/favicons/apple-touch-icon.webp',
234
+ 'icons/icon-192.webp': 'assets/favicons/icon-192.webp',
235
+ 'icons/icon-512.webp': 'assets/favicons/icon-512.webp',
236
+ 'logos/logo-square.webp': 'assets/logos/logo-square.webp',
237
+ 'logos/logo-santi020k.webp': 'assets/logos/logo-santi020k.webp',
238
+ 'projects/santi020k-theme/cover.webp': 'assets/projects/santi020k-theme/cover.webp',
239
+ 'projects/santi020k-theme/cover-horizontal.webp': 'assets/projects/santi020k-theme/cover-horizontal.webp',
240
+ 'projects/santi020k-theme/cover-vertical.webp': 'assets/projects/santi020k-theme/cover-vertical.webp',
241
+ 'projects/santi020k-theme/logo.webp': 'assets/projects/santi020k-theme/logo.webp',
242
+ 'projects/santi020k-theme/preview-light.webp': 'assets/projects/santi020k-theme/preview-light.webp'
243
+ }
244
+
245
+ const assetPaths = [
246
+ 'assets/banners/banner-1.png',
247
+ 'assets/banners/banner-1.webp',
248
+ 'assets/banners/banner-2.png',
249
+ 'assets/banners/banner-2.webp',
250
+ 'assets/banners/banner-3.png',
251
+ 'assets/banners/banner-3.webp',
252
+ 'assets/banners/banner-4.png',
253
+ 'assets/banners/banner-4.webp',
254
+ 'assets/banners/banner-5.png',
255
+ 'assets/banners/banner-5.webp',
256
+ 'assets/chrome/icons/icon-light.svg',
257
+ 'assets/chrome/icons/icon-light128.png',
258
+ 'assets/chrome/icons/icon-light16.png',
259
+ 'assets/chrome/icons/icon-light48.png',
260
+ 'assets/chrome/icons/icon-light512.png',
261
+ 'assets/chrome/icons/icon.svg',
262
+ 'assets/chrome/icons/icon128.png',
263
+ 'assets/chrome/icons/icon16.png',
264
+ 'assets/chrome/icons/icon48.png',
265
+ 'assets/chrome/icons/icon512.png',
266
+ 'assets/chrome/images/adaptive_assets_diagonal.webp',
267
+ 'assets/chrome/images/adaptive_background.svg',
268
+ 'assets/chrome/images/dark.png',
269
+ 'assets/chrome/images/light.png',
270
+ 'assets/chrome/images/theme_ntp_background.png',
271
+ 'assets/chrome/images/theme_ntp_background.webp',
272
+ 'assets/chrome/images/theme_ntp_background_light.png',
273
+ 'assets/chrome/images/theme_ntp_background_light.webp',
274
+ 'assets/chrome/images/wallpaper.heic',
275
+ 'assets/chrome/store/marquee-banner-light.png',
276
+ 'assets/chrome/store/marquee-banner.png',
277
+ 'assets/chrome/store/promo-tile-light.png',
278
+ 'assets/chrome/store/promo-tile.png',
279
+ 'assets/chrome/store/screenshot-incognito-light.png',
280
+ 'assets/chrome/store/screenshot-incognito.jpg',
281
+ 'assets/chrome/store/screenshot-incognito.png',
282
+ 'assets/chrome/store/screenshot-main-light.png',
283
+ 'assets/chrome/store/screenshot-main.png',
284
+ 'assets/chrome/store/screenshot-ntp-light.png',
285
+ 'assets/chrome/store/screenshot-ntp.png',
286
+ 'assets/favicons/apple-touch-icon.png',
287
+ 'assets/favicons/apple-touch-icon.webp',
288
+ 'assets/favicons/favicon-16x16.png',
289
+ 'assets/favicons/favicon-16x16.webp',
290
+ 'assets/favicons/favicon-32x32.png',
291
+ 'assets/favicons/favicon-32x32.webp',
292
+ 'assets/favicons/favicon.svg',
293
+ 'assets/favicons/icon-192.png',
294
+ 'assets/favicons/icon-192.webp',
295
+ 'assets/favicons/icon-512.png',
296
+ 'assets/favicons/icon-512.webp',
297
+ 'assets/logos/logo-santi020k-dark.png',
298
+ 'assets/logos/logo-santi020k-dark.svg',
299
+ 'assets/logos/logo-santi020k-dark.webp',
300
+ 'assets/logos/logo-santi020k.png',
301
+ 'assets/logos/logo-santi020k.svg',
302
+ 'assets/logos/logo-santi020k.webp',
303
+ 'assets/logos/logo-square.png',
304
+ 'assets/logos/logo-square.svg',
305
+ 'assets/logos/logo-square.webp',
306
+ 'assets/projects/santi020k-theme/cover-horizontal.webp',
307
+ 'assets/projects/santi020k-theme/cover-v2.webp',
308
+ 'assets/projects/santi020k-theme/cover-vertical.webp',
309
+ 'assets/projects/santi020k-theme/cover.webp',
310
+ 'assets/projects/santi020k-theme/logo.webp',
311
+ 'assets/projects/santi020k-theme/preview-light.webp',
312
+ 'assets/vscode/icon.png',
313
+ 'assets/vscode/icon.svg',
314
+ 'assets/vscode/previews/preview-dark.png',
315
+ 'assets/vscode/previews/preview-dark.svg',
316
+ 'assets/vscode/previews/preview-hc-dark.png',
317
+ 'assets/vscode/previews/preview-hc-light.png',
318
+ 'assets/vscode/previews/preview-light.png',
319
+ 'assets/vscode/previews/preview-light.svg',
320
+ 'assets/wallpapers/wallpaper-1-desktop.png',
321
+ 'assets/wallpapers/wallpaper-1-desktop.webp',
322
+ 'assets/wallpapers/wallpaper-1-mobile.png',
323
+ 'assets/wallpapers/wallpaper-1-mobile.webp',
324
+ 'assets/wallpapers/wallpaper-2-desktop.png',
325
+ 'assets/wallpapers/wallpaper-2-desktop.webp',
326
+ 'assets/wallpapers/wallpaper-2-mobile.png',
327
+ 'assets/wallpapers/wallpaper-2-mobile.webp',
328
+ 'assets/wallpapers/wallpaper-3-desktop.png',
329
+ 'assets/wallpapers/wallpaper-3-desktop.webp',
330
+ 'assets/wallpapers/wallpaper-3-mobile.png',
331
+ 'assets/wallpapers/wallpaper-3-mobile.webp',
332
+ 'assets/wallpapers/wallpaper.png',
333
+ 'assets/wallpapers/wallpaper.webp'
334
+ ]
335
+
336
+ const formatFromPath = path => path.slice(path.lastIndexOf('.') + 1)
337
+
338
+ const categoryFromPath = path => {
339
+ if (path.startsWith('assets/projects/') && path.endsWith('/logo.webp')) {
340
+ return 'logo'
341
+ }
342
+
343
+ if (path.startsWith('assets/projects/') && (path.includes('/cover') || path.includes('/preview'))) {
344
+ return 'preview'
345
+ }
346
+
347
+ if (path.includes('/logos/')) {
348
+ return 'logo'
349
+ }
350
+
351
+ if (path.includes('/favicons/')) {
352
+ return 'favicon'
353
+ }
354
+
355
+ if (path.includes('/banners/')) {
356
+ return 'banner'
357
+ }
358
+
359
+ if (path.includes('/wallpapers/')) {
360
+ return 'wallpaper'
361
+ }
362
+
363
+ if (path.includes('/previews/')) {
364
+ return 'preview'
365
+ }
366
+
367
+ if (path.includes('/screenshots/') || path.includes('/screenshot-')) {
368
+ return 'screenshot'
369
+ }
370
+
371
+ if (path.includes('/store/')) {
372
+ return 'store'
373
+ }
374
+
375
+ if (path.includes('/icons/')) {
376
+ return 'icon'
377
+ }
378
+
379
+ return 'theme'
380
+ }
381
+
382
+ const surfaceFromPath = path => {
383
+ if (path.startsWith('assets/chrome/')) {
384
+ return 'chrome'
385
+ }
386
+
387
+ if (path.startsWith('assets/vscode/')) {
388
+ return 'vscode'
389
+ }
390
+
391
+ if (path.startsWith('assets/projects/')) {
392
+ return 'website'
393
+ }
394
+
395
+ return 'brand'
396
+ }
397
+
398
+ const variantFromPath = path => {
399
+ if (path.includes('light')) {
400
+ return 'light'
401
+ }
402
+
403
+ if (path.includes('dark')) {
404
+ return 'dark'
405
+ }
406
+
407
+ return 'both'
408
+ }
409
+
410
+ const descriptionFromPath = path => {
411
+ const filename = path.slice(path.lastIndexOf('/') + 1)
412
+
413
+ const readableName = filename
414
+ .replace(/\.[^.]+$/, '')
415
+ .replaceAll('-', ' ')
416
+ .replaceAll('_', ' ')
417
+
418
+ return `Santi020k ${readableName} asset`
419
+ }
420
+
421
+ export const assets = assetPaths.map(path => ({
422
+ path,
423
+ category: categoryFromPath(path),
424
+ description: descriptionFromPath(path),
425
+ format: formatFromPath(path),
426
+ variant: variantFromPath(path),
427
+ surface: surfaceFromPath(path)
428
+ }))
429
+
430
+ export const manifest = {
431
+ packageName,
432
+ staticAssets,
433
+ assets
434
+ }
435
+
436
+ export const projects = {
437
+ santi020kTheme: {
438
+ slug: 'santi020k-theme',
439
+ title: 'Santi020k Theme',
440
+ description: 'Designed and shipped a VS Code theme extension with matched dark and light variants, marketplace publishing, registry automation, and a focused documentation site.',
441
+ role: 'Creator',
442
+ startingDate: '28 Apr 2026',
443
+ githubUrl: 'https://github.com/santi020k/santi020k-theme',
444
+ liveDemoUrl: 'https://theme.santi020k.com/',
445
+ typesId: 'personal',
446
+ impactMetrics: [
447
+ 'Published across the VS Code Marketplace and Open VSX',
448
+ 'Built dark and light variants from one coherent color language',
449
+ 'Automated validation, packaging, and registry publishing'
450
+ ],
451
+ technologies: [
452
+ 'Visual Studio Code',
453
+ 'VS Code Extension',
454
+ 'Theme Design',
455
+ 'JavaScript',
456
+ 'Node.js',
457
+ 'Vitest',
458
+ 'ESLint',
459
+ 'CI-CD',
460
+ 'GitHub Actions',
461
+ 'Open Source',
462
+ 'Developer Experience (DX)',
463
+ 'Developer Documentation',
464
+ 'Accessibility',
465
+ 'Testing',
466
+ 'Design Systems'
467
+ ],
468
+ coverImage: {
469
+ src: 'assets/projects/santi020k-theme/cover.webp',
470
+ horizontal: 'assets/projects/santi020k-theme/cover-horizontal.webp',
471
+ vertical: 'assets/projects/santi020k-theme/cover-vertical.webp',
472
+ logo: 'assets/projects/santi020k-theme/logo.webp',
473
+ logoAspect: 'square',
474
+ logoSurface: 'dark',
475
+ alt: 'Santi020k Theme logo on a deep indigo geometric cover with editor UI artwork'
476
+ },
477
+ previewImage: 'assets/projects/santi020k-theme/preview-light.webp'
478
+ }
479
+ }
480
+
481
+ export const voice = {
482
+ personality: ['Direct', 'Concise', 'Technical', 'Personal', 'Honest'],
483
+ vocabulary: [
484
+ 'calm systems',
485
+ 'clear delivery',
486
+ 'architecture',
487
+ 'automation',
488
+ 'developer experience',
489
+ 'resilient',
490
+ 'well-crafted'
491
+ ],
492
+ avoidVocabulary: [
493
+ 'leverage',
494
+ 'synergy',
495
+ 'utilize',
496
+ 'seamless',
497
+ 'robust',
498
+ 'cutting-edge'
499
+ ],
500
+ toneByContext: {
501
+ homepage: 'Confident, identity-first, one clear call to action',
502
+ docs: 'Minimal and practical',
503
+ code: 'Explain why, keep examples short',
504
+ error: 'Light and friendly, never robotic'
505
+ }
506
+ }
507
+
508
+ export const config = {
509
+ name: 'Santi020k',
510
+ packageName,
511
+ description: 'Design tokens, logos, icons, wallpapers, and theme-family assets for Santi020k projects.',
512
+ colors,
513
+ typography,
514
+ assets: manifest,
515
+ projects,
516
+ voice,
517
+ darkMode: 'data-theme',
518
+ darkModeVariant: '[data-theme="dark"] &',
519
+ accessibilityTarget: 'AA',
520
+ meta: {
521
+ css: '@santi020k/theme/tokens.css',
522
+ tokens: '@santi020k/theme/tokens.json',
523
+ tailwind: '@santi020k/theme/tailwind'
524
+ }
525
+ }
526
+
527
+ export const getAsset = path => getAssetByPath(manifest, path)
528
+
529
+ export const getAssets = category => getAssetsByCategory(manifest, category)
530
+
531
+ export const getSurfaceAssets = surface => getAssetsBySurface(manifest, surface)
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@santi020k/theme",
3
+ "version": "1.0.0",
4
+ "description": "Santi020k design tokens and shared brand assets for websites, apps, and theme surfaces.",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "module": "./index.js",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./index.js"
13
+ },
14
+ "./typography.css": "./typography.css",
15
+ "./tokens.css": "./tokens/tokens.css",
16
+ "./tokens.json": "./tokens/tokens.json",
17
+ "./site": {
18
+ "types": "./site.d.ts",
19
+ "import": "./site.js"
20
+ },
21
+ "./site.css": "./site.css",
22
+ "./tailwind": {
23
+ "types": "./tailwind.d.ts",
24
+ "import": "./tailwind.js"
25
+ },
26
+ "./assets/*": "./assets/*",
27
+ "./docs/*": "./docs/*",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "assets",
32
+ "docs",
33
+ "tokens",
34
+ "site.css",
35
+ "site.d.ts",
36
+ "site.js",
37
+ "typography.css",
38
+ "index.d.ts",
39
+ "index.js",
40
+ "tailwind.d.ts",
41
+ "tailwind.js",
42
+ "README.md"
43
+ ],
44
+ "dependencies": {
45
+ "@santi020k/theme-core": "^1.0.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "engines": {
51
+ "node": ">=22.18.0"
52
+ },
53
+ "license": "MIT",
54
+ "author": {
55
+ "name": "Santiago Molina",
56
+ "url": "https://santi020k.com"
57
+ },
58
+ "funding": {
59
+ "type": "github",
60
+ "url": "https://github.com/sponsors/santi020k"
61
+ },
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "https://github.com/santi020k/santi020k-theme",
65
+ "directory": "packages/theme"
66
+ },
67
+ "bugs": {
68
+ "url": "https://github.com/santi020k/santi020k-theme/issues"
69
+ },
70
+ "homepage": "https://theme.santi020k.com",
71
+ "scripts": {
72
+ "build": "node --check index.js && node --check site.js && node --check tailwind.js",
73
+ "validate": "pnpm run build"
74
+ }
75
+ }
package/site.css ADDED
@@ -0,0 +1,84 @@
1
+ @import './typography.css';
2
+
3
+ :root,
4
+ :root[data-theme="dark"] {
5
+ color-scheme: dark;
6
+ --theme-bg: #110c1d;
7
+ --surface: #1c1528;
8
+ --surface-muted: #231d30;
9
+ --surface-strong: #322b40;
10
+ --line: #494158;
11
+ --ink: #dfdde3;
12
+ --ink-soft: #b6b2bd;
13
+ --ink-muted: #8d8896;
14
+ --brand: #945df4;
15
+ --brand-solid: #5a0fdb;
16
+ --brand-hover: #752df0;
17
+ --brand-mark-ink: #ffffff;
18
+ --accent: #b48df7;
19
+ --success: #7daea3;
20
+ --warning: #e8b44a;
21
+ --danger: #ea6962;
22
+ --glow-rgb: 154 102 244;
23
+ --btn-secondary-bg: rgb(28 21 40 / 0.72);
24
+ --section-divider: rgb(73 65 88 / 0.7);
25
+ --swatch-circle-border: rgb(223 221 227 / 0.22);
26
+ }
27
+
28
+ :root[data-theme="light"] {
29
+ color-scheme: light;
30
+ --theme-bg: #f8f6fd;
31
+ --surface: #f0edf9;
32
+ --surface-muted: #eae7f5;
33
+ --surface-strong: #e3dff0;
34
+ --line: #d3cde6;
35
+ --ink: #302e36;
36
+ --ink-soft: #403850;
37
+ --ink-muted: #9880c0;
38
+ --brand: #6319be;
39
+ --brand-solid: #5a14b0;
40
+ --brand-hover: #7730b8;
41
+ --brand-mark-ink: #ffffff;
42
+ --accent: #7030b0;
43
+ --success: #28a745;
44
+ --warning: #c07a10;
45
+ --danger: #c0392b;
46
+ --glow-rgb: 99 25 190;
47
+ --btn-secondary-bg: rgb(248 246 253 / 0.85);
48
+ --section-divider: rgb(211 205 230 / 0.7);
49
+ --swatch-circle-border: rgb(58 37 86 / 0.15);
50
+ }
51
+
52
+ :root[data-santi020k-site="hub"],
53
+ :root[data-santi020k-site="hub"][data-theme="dark"] {
54
+ --theme-bg: #0f0b19;
55
+ --surface: #1a1426;
56
+ --surface-muted: #221a31;
57
+ --surface-strong: #2e2440;
58
+ --line: #453a55;
59
+ --line-soft: rgb(69 58 85 / 0.58);
60
+ --ink: #f1edf8;
61
+ --ink-soft: #c7c0d2;
62
+ --ink-muted: #91889f;
63
+ --brand: #9b66ff;
64
+ --brand-solid: #6d28f3;
65
+ --brand-hover: #7f3cff;
66
+ --accent: #c7a4ff;
67
+ --success: #78d2bf;
68
+ --chrome: #7cc9b9;
69
+ --shadow: rgb(0 0 0 / 0.34);
70
+ --btn-secondary-bg: rgb(26 20 38 / 0.76);
71
+ --section-divider: rgb(69 58 85 / 0.66);
72
+ }
73
+
74
+ :root[data-santi020k-site="hub"][data-theme="light"] {
75
+ --line-soft: rgb(177 165 205 / 0.64);
76
+ --ink-muted: #725b92;
77
+ --success: #1f7f67;
78
+ --warning: #9a610c;
79
+ --danger: #b33a30;
80
+ --chrome: #21796a;
81
+ --shadow: rgb(58 37 86 / 0.16);
82
+ --btn-secondary-bg: rgb(248 246 253 / 0.88);
83
+ --section-divider: rgb(211 205 230 / 0.8);
84
+ }