@purestack/ts-components 1.1.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -0
  3. package/dist/ts-components.d.mts +1662 -0
  4. package/dist/ts-components.d.mts.map +1 -0
  5. package/dist/ts-components.mjs +9200 -0
  6. package/dist/ts-components.mjs.map +1 -0
  7. package/package.json +130 -0
  8. package/src/defineComponents.ts +87 -0
  9. package/src/index.ts +270 -0
  10. package/src/registerStyles.ts +63 -0
  11. package/src/standard/alertBox/alertBox.ts +64 -0
  12. package/src/standard/autoId.ts +25 -0
  13. package/src/standard/badge/badge.ts +41 -0
  14. package/src/standard/badge/badgeStyle.ts +21 -0
  15. package/src/standard/barChart/barChart.ts +338 -0
  16. package/src/standard/barChart/barChartStyle.ts +81 -0
  17. package/src/standard/btn/btn.ts +191 -0
  18. package/src/standard/btn/btnStyle.ts +59 -0
  19. package/src/standard/btnGroup/btnGroup.ts +208 -0
  20. package/src/standard/btnGroup/btnGroupStyle.ts +85 -0
  21. package/src/standard/chart/cartesianChart.ts +99 -0
  22. package/src/standard/chart/chartDefaults.ts +25 -0
  23. package/src/standard/chart/chartUtils.ts +84 -0
  24. package/src/standard/classicLogo/classicLogo.ts +429 -0
  25. package/src/standard/classicLogo/classicLogoStyle.ts +292 -0
  26. package/src/standard/componentVariant.ts +153 -0
  27. package/src/standard/composer/composer.ts +1035 -0
  28. package/src/standard/composer/composerCanvasInitialStyle.ts +225 -0
  29. package/src/standard/composer/composerCanvasRevertStyle.ts +14 -0
  30. package/src/standard/composer/composerCanvasSharedStyle.ts +60 -0
  31. package/src/standard/composer/composerCanvasStyleTypes.ts +8 -0
  32. package/src/standard/composer/composerCanvasThemeStyle.ts +53 -0
  33. package/src/standard/composer/composerStyle.ts +156 -0
  34. package/src/standard/composer/emailHtmlPolicy.ts +474 -0
  35. package/src/standard/consent/consent.ts +173 -0
  36. package/src/standard/consent/consentStyle.ts +44 -0
  37. package/src/standard/contactForm/contactForm.ts +135 -0
  38. package/src/standard/contactForm/contactFormStyle.ts +116 -0
  39. package/src/standard/doughnutChart/doughnutChart.ts +527 -0
  40. package/src/standard/doughnutChart/doughnutChartStyle.ts +64 -0
  41. package/src/standard/dropFiles/dropFiles.ts +480 -0
  42. package/src/standard/dropFiles/dropFilesStyle.ts +171 -0
  43. package/src/standard/expandablePanel/expandablePanel.ts +106 -0
  44. package/src/standard/expandablePanel/expandablePanelStyle.ts +56 -0
  45. package/src/standard/flex/flex.ts +269 -0
  46. package/src/standard/flex/flexStyle.ts +556 -0
  47. package/src/standard/footer/footer.ts +93 -0
  48. package/src/standard/footer/footerStyle.ts +39 -0
  49. package/src/standard/form/autoCompleteInput.ts +567 -0
  50. package/src/standard/form/form.ts +205 -0
  51. package/src/standard/form/formCheck.ts +96 -0
  52. package/src/standard/form/formInputField.ts +184 -0
  53. package/src/standard/form/formSelectField.ts +152 -0
  54. package/src/standard/form/formStyle.ts +585 -0
  55. package/src/standard/form/multiAutoCompleteInput.ts +880 -0
  56. package/src/standard/grid/grid.ts +177 -0
  57. package/src/standard/grid/gridStyle.ts +56 -0
  58. package/src/standard/icon/icon.ts +96 -0
  59. package/src/standard/icon/iconStyle.ts +50 -0
  60. package/src/standard/landing/codeShowcase.ts +54 -0
  61. package/src/standard/landing/comparisonColumn.ts +30 -0
  62. package/src/standard/landing/comparisonFeature.ts +22 -0
  63. package/src/standard/landing/comparisonTable.ts +38 -0
  64. package/src/standard/landing/ctaSection.ts +59 -0
  65. package/src/standard/landing/featureCard.ts +41 -0
  66. package/src/standard/landing/landing.ts +52 -0
  67. package/src/standard/landing/landingBand.ts +217 -0
  68. package/src/standard/landing/landingSection.ts +43 -0
  69. package/src/standard/landing/landingTypes.ts +141 -0
  70. package/src/standard/landing/metricItem.ts +25 -0
  71. package/src/standard/landing/metricStrip.ts +20 -0
  72. package/src/standard/lineChart/lineChart.ts +462 -0
  73. package/src/standard/lineChart/lineChartStyle.ts +91 -0
  74. package/src/standard/logo/logo.ts +192 -0
  75. package/src/standard/logo/logoStyle.ts +164 -0
  76. package/src/standard/modal/modal.ts +171 -0
  77. package/src/standard/modal/modalStore.ts +154 -0
  78. package/src/standard/modal/modalStyle.ts +190 -0
  79. package/src/standard/navMenu/navMenu.ts +278 -0
  80. package/src/standard/navMenu/navMenuStyle.ts +323 -0
  81. package/src/standard/pageLinks/pageLinks.ts +90 -0
  82. package/src/standard/pageScript/pageScript.ts +155 -0
  83. package/src/standard/pageToc/pageToc.ts +154 -0
  84. package/src/standard/pageToc/pageTocStyle.ts +372 -0
  85. package/src/standard/panel/panel.ts +55 -0
  86. package/src/standard/panel/panelStyle.ts +15 -0
  87. package/src/standard/pricing/pricing.ts +146 -0
  88. package/src/standard/searchBox/searchBox.ts +54 -0
  89. package/src/standard/searchBox/searchBoxStyle.ts +180 -0
  90. package/src/standard/sectionHeader/sectionHeader.ts +99 -0
  91. package/src/standard/signIn/signIn.ts +167 -0
  92. package/src/standard/signIn/signInStyle.ts +70 -0
  93. package/src/standard/tabs/tabs.ts +193 -0
  94. package/src/standard/tabs/tabsStyle.ts +231 -0
  95. package/src/standard/themeToggle/themeToggle.ts +33 -0
  96. package/src/standard/themeToggle/themeToggleStyle.ts +88 -0
  97. package/src/standard/toast/toastHost.ts +42 -0
  98. package/src/standard/toast/toastStore.ts +50 -0
  99. package/src/standard/topBar/topBar.ts +101 -0
  100. package/src/standard/topBar/topBarStyle.ts +169 -0
  101. package/src/standard/virtualList/variableVirtualTable.ts +371 -0
  102. package/src/standard/virtualList/virtualList.ts +423 -0
  103. package/src/standard/virtualList/virtualListStyle.ts +34 -0
  104. package/src/standard/virtualList/virtualTable.ts +179 -0
  105. package/src/standard/virtualList/virtualTableStyle.ts +24 -0
  106. package/tsconfig.json +23 -0
@@ -0,0 +1,429 @@
1
+ import {
2
+ type ClassicLogoConfig,
3
+ type TsSsgContext,
4
+ tryResolveTsSsgContext,
5
+ } from '@purestack/ts-common'
6
+ import {
7
+ getCurrentThemePaletteVar,
8
+ getThemePaletteVar,
9
+ normalizeThemeVariableReference,
10
+ } from '@purestack/ts-style'
11
+ import {
12
+ type ComputedRef,
13
+ computed,
14
+ defineComponent,
15
+ flatten,
16
+ html,
17
+ type RefOrValue,
18
+ unref,
19
+ } from 'regor'
20
+
21
+ const logoTemplate = html`<div class="classic-logo" :style="logo.layoutStyle">
22
+ <a class="classic-logo__link" :href="logo.href === null ? undefined : (logo.href ?? '/')" :aria-label="logo.ariaLabel">
23
+ <span class="classic-logo__glyph" :style="logo.glyphStyle" aria-hidden="true">
24
+ <Icon :name="logo.icon" r-if="logo.icon"/>
25
+ <span class="classic-logo__glyph-mark" r-else></span>
26
+ </span>
27
+ <span class="classic-logo__stack">
28
+ <span class="classic-logo__brand">
29
+ <span
30
+ r-for="brandLetter in logo.brandLetters"
31
+ class="classic-logo__brand-letter"
32
+ :style="brandLetter.style"
33
+ >
34
+ {{ brandLetter.value }}
35
+ </span>
36
+ </span>
37
+ <span class="classic-logo__subtitle" r-if="logo.subtitle">
38
+ <span
39
+ r-for="subtitleLetter in logo.subtitleLetters"
40
+ class="classic-logo__subtitle-letter"
41
+ :style="subtitleLetter.style"
42
+ >
43
+ {{ subtitleLetter.value }}
44
+ </span>
45
+ </span>
46
+ </span>
47
+ </a>
48
+ </div>`
49
+
50
+ interface LogoLetter {
51
+ value: string
52
+ isSpace?: boolean
53
+ style?: Record<string, string>
54
+ }
55
+
56
+ type LogoResponsiveLength = {
57
+ base?: string
58
+ sm?: string
59
+ md?: string
60
+ lg?: string
61
+ xl?: string
62
+ }
63
+
64
+ const DEFAULT_BRAND_FILL = getThemePaletteVar(
65
+ 'semanticTone.accent.button.rest.background',
66
+ )
67
+ const DEFAULT_SUBTITLE_FILL = getCurrentThemePaletteVar('text.subtle')
68
+
69
+ interface ResolvedClassicLogo {
70
+ brand?: string
71
+ letterColors?: string
72
+ subtitleLetterColors?: string
73
+ colors?: string[]
74
+ logoBackground?: number
75
+ logoForeground?: number
76
+ brandLetters?: LogoLetter[]
77
+ subtitle?: string
78
+ subtitleLetters?: LogoLetter[]
79
+ glyphStyle?: Record<string, string>
80
+ layoutStyle?: Record<string, string>
81
+ brandSize?: string
82
+ brandSizeSm?: string
83
+ brandSizeMd?: string
84
+ brandSizeLg?: string
85
+ brandSizeXl?: string
86
+ subtitleSize?: string
87
+ subtitleSizeSm?: string
88
+ subtitleSizeMd?: string
89
+ subtitleSizeLg?: string
90
+ subtitleSizeXl?: string
91
+ iconSize?: string
92
+ iconSizeSm?: string
93
+ iconSizeMd?: string
94
+ iconSizeLg?: string
95
+ iconSizeXl?: string
96
+ subtitleInset?: string
97
+ subtitleInsetSm?: string
98
+ subtitleInsetMd?: string
99
+ subtitleInsetLg?: string
100
+ subtitleInsetXl?: string
101
+ href?: string | null
102
+ icon?: string
103
+ ariaLabel?: string
104
+ }
105
+
106
+ function resolveClassicLogo(
107
+ props: Partial<ClassicLogoConfig>,
108
+ context: TsSsgContext | undefined,
109
+ ): ResolvedClassicLogo {
110
+ const brand = props.brand
111
+ const letterColors = props.letterColors
112
+ const subtitleLetterColors = props.subtitleLetterColors
113
+ const colors = normalizeLogoColors(props.colors)
114
+ const logoBackground = props.logoBackground
115
+ const logoForeground = props.logoForeground
116
+ const brandLetters = buildBrandLetters(brand, letterColors, colors)
117
+ const subtitle = props.subtitle
118
+ const subtitleLetters = buildLogoLetters(
119
+ subtitle,
120
+ subtitleLetterColors,
121
+ colors,
122
+ DEFAULT_SUBTITLE_FILL,
123
+ )
124
+ const glyphStyle = resolveGlyphStyle(colors, logoBackground, logoForeground)
125
+ const layoutStyle = resolveLogoLayoutStyle(props)
126
+ const href = props.href
127
+ ? (context?.resolvePublicHref(props.href) ?? props.href)
128
+ : props.href
129
+ const icon = props.icon
130
+ const ariaLabel = props.ariaLabel
131
+
132
+ return {
133
+ brand,
134
+ letterColors,
135
+ subtitleLetterColors,
136
+ colors,
137
+ logoBackground,
138
+ logoForeground,
139
+ brandLetters,
140
+ subtitle,
141
+ subtitleLetters,
142
+ glyphStyle,
143
+ layoutStyle,
144
+ brandSize: props.brandSize,
145
+ brandSizeSm: props.brandSizeSm,
146
+ brandSizeMd: props.brandSizeMd,
147
+ brandSizeLg: props.brandSizeLg,
148
+ brandSizeXl: props.brandSizeXl,
149
+ subtitleSize: props.subtitleSize,
150
+ subtitleSizeSm: props.subtitleSizeSm,
151
+ subtitleSizeMd: props.subtitleSizeMd,
152
+ subtitleSizeLg: props.subtitleSizeLg,
153
+ subtitleSizeXl: props.subtitleSizeXl,
154
+ iconSize: props.iconSize,
155
+ iconSizeSm: props.iconSizeSm,
156
+ iconSizeMd: props.iconSizeMd,
157
+ iconSizeLg: props.iconSizeLg,
158
+ iconSizeXl: props.iconSizeXl,
159
+ subtitleInset: props.subtitleInset,
160
+ subtitleInsetSm: props.subtitleInsetSm,
161
+ subtitleInsetMd: props.subtitleInsetMd,
162
+ subtitleInsetLg: props.subtitleInsetLg,
163
+ subtitleInsetXl: props.subtitleInsetXl,
164
+ href,
165
+ icon,
166
+ ariaLabel: ariaLabel || brand,
167
+ }
168
+ }
169
+
170
+ function normalizeLogoColors(colors: string[] | undefined) {
171
+ return colors?.map((color) => normalizeThemeVariableReference(color))
172
+ }
173
+
174
+ function buildBrandLetters(
175
+ brand: string | undefined,
176
+ letterColors: string | undefined,
177
+ colors: string[] | undefined,
178
+ ): LogoLetter[] | undefined {
179
+ return buildLogoLetters(brand, letterColors, colors, DEFAULT_BRAND_FILL)
180
+ }
181
+
182
+ function buildLogoLetters(
183
+ text: string | undefined,
184
+ letterColors: string | undefined,
185
+ colors: string[] | undefined,
186
+ defaultFill: string,
187
+ ): LogoLetter[] | undefined {
188
+ const letters: LogoLetter[] = []
189
+ let colorCursor = 0
190
+
191
+ if (text) {
192
+ for (const character of [...text]) {
193
+ if (character === ' ') {
194
+ letters.push({
195
+ value: '\u00A0',
196
+ isSpace: true,
197
+ })
198
+ continue
199
+ }
200
+ letters.push({
201
+ value: character,
202
+ style: resolveBrandLetterStyle(
203
+ letterColors,
204
+ colors,
205
+ colorCursor,
206
+ defaultFill,
207
+ ),
208
+ })
209
+ colorCursor += 1
210
+ }
211
+ }
212
+
213
+ return letters.length > 0 ? letters : undefined
214
+ }
215
+
216
+ function resolveBrandLetterStyle(
217
+ letterColors: string | undefined,
218
+ colors: string[] | undefined,
219
+ letterIndex: number,
220
+ defaultFill: string,
221
+ ): Record<string, string> | undefined {
222
+ if (!letterColors) {
223
+ return {
224
+ backgroundImage: defaultFill,
225
+ backgroundColor: defaultFill,
226
+ }
227
+ }
228
+ const colorIndexLiteral =
229
+ letterColors?.[letterIndex] ??
230
+ (letterColors && letterColors.length > 0
231
+ ? letterColors[letterColors.length - 1]
232
+ : undefined)
233
+ if (colorIndexLiteral === undefined) return undefined
234
+ const colorIndex = Number.parseInt(colorIndexLiteral, 10)
235
+ if (Number.isNaN(colorIndex) || colorIndex < 0) return undefined
236
+ const color = colors?.[colorIndex]
237
+ if (!color) {
238
+ return {
239
+ backgroundImage: defaultFill,
240
+ backgroundColor: defaultFill,
241
+ }
242
+ }
243
+ return {
244
+ backgroundImage: color,
245
+ backgroundColor: color,
246
+ }
247
+ }
248
+
249
+ function resolveGlyphStyle(
250
+ colors: string[] | undefined,
251
+ logoBackground: number | undefined,
252
+ logoForeground: number | undefined,
253
+ ): Record<string, string> | undefined {
254
+ const style: Record<string, string> = {}
255
+ const background = resolveIndexedLogoColor(colors, logoBackground)
256
+ const foreground = resolveIndexedLogoColor(colors, logoForeground)
257
+
258
+ if (background) {
259
+ style['--ps-classic-logo-glyph-background'] = background
260
+ }
261
+
262
+ if (foreground) {
263
+ style['--ps-classic-logo-glyph-foreground'] = foreground
264
+ }
265
+
266
+ return Object.keys(style).length > 0 ? style : undefined
267
+ }
268
+
269
+ function resolveIndexedLogoColor(
270
+ colors: string[] | undefined,
271
+ colorIndex: number | undefined,
272
+ ): string | undefined {
273
+ if (colorIndex === undefined) return undefined
274
+ if (!Number.isInteger(colorIndex) || colorIndex < 0) return undefined
275
+ return colors?.[colorIndex]
276
+ }
277
+
278
+ function resolveLogoLayoutStyle(
279
+ props: Partial<ClassicLogoConfig>,
280
+ ): Record<string, string> | undefined {
281
+ const style: Record<string, string> = {}
282
+
283
+ assignResponsiveLengthVars(
284
+ style,
285
+ '--ps-classic-logo-brand-size',
286
+ resolveResponsiveLogoLength({
287
+ base: props.brandSize,
288
+ sm: props.brandSizeSm,
289
+ md: props.brandSizeMd,
290
+ lg: props.brandSizeLg,
291
+ xl: props.brandSizeXl,
292
+ }),
293
+ )
294
+ assignResponsiveLengthVars(
295
+ style,
296
+ '--ps-classic-logo-subtitle-size',
297
+ resolveResponsiveLogoLength({
298
+ base: props.subtitleSize,
299
+ sm: props.subtitleSizeSm,
300
+ md: props.subtitleSizeMd,
301
+ lg: props.subtitleSizeLg,
302
+ xl: props.subtitleSizeXl,
303
+ }),
304
+ )
305
+ assignResponsiveLengthVars(
306
+ style,
307
+ '--ps-classic-logo-icon-size',
308
+ resolveResponsiveLogoLength({
309
+ base: props.iconSize,
310
+ sm: props.iconSizeSm,
311
+ md: props.iconSizeMd,
312
+ lg: props.iconSizeLg,
313
+ xl: props.iconSizeXl,
314
+ }),
315
+ )
316
+ assignResponsiveLengthVars(
317
+ style,
318
+ '--ps-classic-logo-subtitle-inset',
319
+ resolveResponsiveLogoLength({
320
+ base: props.subtitleInset,
321
+ sm: props.subtitleInsetSm,
322
+ md: props.subtitleInsetMd,
323
+ lg: props.subtitleInsetLg,
324
+ xl: props.subtitleInsetXl,
325
+ }),
326
+ )
327
+
328
+ return Object.keys(style).length > 0 ? style : undefined
329
+ }
330
+
331
+ function resolveResponsiveLogoLength(
332
+ value: LogoResponsiveLength,
333
+ ): LogoResponsiveLength | undefined {
334
+ const base = resolveOptionalCssLength(value.base)
335
+ const sm = resolveOptionalCssLength(value.sm) ?? base
336
+ const md = resolveOptionalCssLength(value.md) ?? sm
337
+ const lg = resolveOptionalCssLength(value.lg) ?? md
338
+ const xl = resolveOptionalCssLength(value.xl) ?? lg
339
+
340
+ if (!base && !sm && !md && !lg && !xl) return undefined
341
+
342
+ return { base, sm, md, lg, xl }
343
+ }
344
+
345
+ function resolveOptionalCssLength(
346
+ value: string | undefined,
347
+ ): string | undefined {
348
+ if (typeof value !== 'string') return undefined
349
+ const normalized = value.trim()
350
+ return normalized.length > 0 ? normalized : undefined
351
+ }
352
+
353
+ function assignResponsiveLengthVars(
354
+ style: Record<string, string>,
355
+ variableName: string,
356
+ value: LogoResponsiveLength | undefined,
357
+ ) {
358
+ if (!value) return
359
+ if (value.base) style[variableName] = value.base
360
+ if (value.sm) style[`${variableName}-sm`] = value.sm
361
+ if (value.md) style[`${variableName}-md`] = value.md
362
+ if (value.lg) style[`${variableName}-lg`] = value.lg
363
+ if (value.xl) style[`${variableName}-xl`] = value.xl
364
+ }
365
+
366
+ type ReactiveClassicLogoOptions = {
367
+ [K in keyof ClassicLogoConfig]?: RefOrValue<ClassicLogoConfig[K]>
368
+ }
369
+
370
+ export interface ClassicLogo extends ReactiveClassicLogoOptions {
371
+ config?: RefOrValue<Partial<ClassicLogoConfig>>
372
+ logo?: ComputedRef<ResolvedClassicLogo>
373
+ }
374
+
375
+ function defineClassicLogoComponent() {
376
+ return defineComponent<ClassicLogo>(logoTemplate, {
377
+ props: [
378
+ 'config',
379
+ 'brand',
380
+ 'letterColors',
381
+ 'subtitleLetterColors',
382
+ 'colors',
383
+ 'logoBackground',
384
+ 'logoForeground',
385
+ 'brandSize',
386
+ 'brandSizeSm',
387
+ 'brandSizeMd',
388
+ 'brandSizeLg',
389
+ 'brandSizeXl',
390
+ 'subtitleSize',
391
+ 'subtitleSizeSm',
392
+ 'subtitleSizeMd',
393
+ 'subtitleSizeLg',
394
+ 'subtitleSizeXl',
395
+ 'iconSize',
396
+ 'iconSizeSm',
397
+ 'iconSizeMd',
398
+ 'iconSizeLg',
399
+ 'iconSizeXl',
400
+ 'subtitleInset',
401
+ 'subtitleInsetSm',
402
+ 'subtitleInsetMd',
403
+ 'subtitleInsetLg',
404
+ 'subtitleInsetXl',
405
+ 'subtitle',
406
+ 'href',
407
+ 'icon',
408
+ 'ariaLabel',
409
+ ],
410
+ context: (head) => ({
411
+ logo: computed(() => {
412
+ const { config, ...direct } = flatten(head.props)
413
+ const options = {
414
+ ...flatten(unref(config) ?? {}),
415
+ ...Object.fromEntries(
416
+ Object.entries(direct).filter(([, value]) => value !== undefined),
417
+ ),
418
+ }
419
+ return resolveClassicLogo(options, tryResolveTsSsgContext(head))
420
+ }),
421
+ }),
422
+ })
423
+ }
424
+
425
+ export function defineClassicLogoComponents() {
426
+ return {
427
+ classicLogo: defineClassicLogoComponent(),
428
+ }
429
+ }
@@ -0,0 +1,292 @@
1
+ import type { ThemePalette } from '@purestack/ts-style'
2
+ import {
3
+ BREAKPOINTS,
4
+ mediaMin,
5
+ styleBuilder,
6
+ type ThemeMode,
7
+ themes,
8
+ } from '@purestack/ts-style'
9
+
10
+ const LOGO_DEFAULTS = {
11
+ brandSize: {
12
+ base: '1.5rem',
13
+ sm: '1.5rem',
14
+ md: '1.75rem',
15
+ lg: '2rem',
16
+ xl: '2rem',
17
+ },
18
+ subtitleSize: {
19
+ base: '0.7rem',
20
+ sm: '0.7rem',
21
+ md: '0.74rem',
22
+ lg: '0.77rem',
23
+ xl: '0.77rem',
24
+ },
25
+ iconSize: {
26
+ base: '2rem',
27
+ sm: '2rem',
28
+ md: '2.5rem',
29
+ lg: '2.85rem',
30
+ xl: '2.85rem',
31
+ },
32
+ subtitleInset: {
33
+ base: '0.03125rem',
34
+ sm: '0.03125rem',
35
+ md: '0.0625rem',
36
+ lg: '0.0625rem',
37
+ xl: '0.0625rem',
38
+ },
39
+ } as const
40
+
41
+ type LogoResponsiveToken =
42
+ | 'brand-size'
43
+ | 'subtitle-size'
44
+ | 'icon-size'
45
+ | 'subtitle-inset'
46
+
47
+ type LogoBreakpoint = 'sm' | 'md' | 'lg' | 'xl'
48
+
49
+ export function registerClassicLogoStyles() {
50
+ themes.forEach((theme, palette) => {
51
+ registerClassicLogoShellStyles(theme, palette)
52
+ registerClassicLogoTextStyles(theme, palette)
53
+ registerClassicLogoResponsiveStyles(theme)
54
+ registerClassicLogoInteractiveStyles(theme, palette)
55
+ })
56
+ }
57
+
58
+ export function registerClassicLogoShellStyles(
59
+ theme: ThemeMode,
60
+ palette: ThemePalette,
61
+ ) {
62
+ styleBuilder
63
+ .select('.classic-logo', theme)
64
+ .display('inline-block')
65
+ .width('fit-content')
66
+
67
+ styleBuilder
68
+ .select('.classic-logo__link', theme)
69
+ .display('inline-flex')
70
+ .alignItems('center')
71
+ .gap('0.25rem')
72
+ .padding('0.25rem 0.5rem')
73
+ .borderRadius(palette.radii.sm)
74
+ .border(`1px solid ${palette.current.border.subtle}`)
75
+ .textDecoration('none')
76
+ .maxWidth('100%')
77
+ .background(palette.current.surface.rest.background)
78
+
79
+ styleBuilder
80
+ .select('.classic-logo__link:hover', theme)
81
+ .boxShadow(palette.effect.interactiveShadow)
82
+
83
+ styleBuilder
84
+ .select('.classic-logo__glyph', theme)
85
+ .width(resolveLogoVar('icon-size'))
86
+ .height(resolveLogoVar('icon-size'))
87
+ .minWidth(resolveLogoVar('icon-size'))
88
+ .minHeight(resolveLogoVar('icon-size'))
89
+ .borderRadius(palette.radii.md)
90
+ .display('grid')
91
+ .placeItems('center')
92
+ .position('relative')
93
+ .background(
94
+ `var(--ps-classic-logo-glyph-background, ${palette.current.button.rest.background})`,
95
+ )
96
+ .backgroundColor(
97
+ `var(--ps-classic-logo-glyph-background, ${palette.current.button.rest.background})`,
98
+ )
99
+ .border(`1px solid ${palette.current.button.rest.border}`)
100
+ .color(
101
+ `var(--ps-classic-logo-glyph-foreground, ${palette.current.button.rest.text})`,
102
+ )
103
+
104
+ styleBuilder
105
+ .select('.classic-logo__glyph > .icon', theme)
106
+ .width('80%')
107
+ .height('80%')
108
+
109
+ styleBuilder
110
+ .select('.classic-logo__glyph-mark', theme)
111
+ .position('absolute')
112
+ .inset('30%')
113
+ .borderRadius('0.25rem')
114
+ .backgroundColor('currentColor')
115
+ }
116
+
117
+ export function registerClassicLogoTextStyles(
118
+ theme: ThemeMode,
119
+ palette: ThemePalette,
120
+ ) {
121
+ styleBuilder
122
+ .select('.classic-logo__stack', theme)
123
+ .display('inline-grid')
124
+ .justifyItems('stretch')
125
+ .gap('0')
126
+ .minWidth('0')
127
+ .textTransform('uppercase')
128
+ .letterSpacing('0')
129
+ .lineHeight('1')
130
+ .whiteSpace('nowrap')
131
+
132
+ styleBuilder
133
+ .select('.classic-logo__brand', theme)
134
+ .display('flex')
135
+ .alignItems('center')
136
+ .justifyContent('space-between')
137
+ .fontSize(resolveLogoVar('brand-size'))
138
+ .fontWeight('800')
139
+
140
+ styleBuilder
141
+ .select('.classic-logo__brand-letter', theme)
142
+ .whiteSpace('nowrap')
143
+ .backgroundClip('text')
144
+ .webkitBackgroundClip('text')
145
+ .color('transparent')
146
+ .webkitTextFillColor('transparent')
147
+
148
+ styleBuilder
149
+ .select('.classic-logo__subtitle', theme)
150
+ .display('flex')
151
+ .justifyContent('space-between')
152
+ .fontSize(resolveLogoVar('subtitle-size'))
153
+ .fontWeight('700')
154
+ .color(palette.current.text.subtle)
155
+ .marginLeft(resolveLogoVar('subtitle-inset'))
156
+ .marginRight(resolveLogoVar('subtitle-inset'))
157
+
158
+ styleBuilder
159
+ .select('.classic-logo__subtitle-letter', theme)
160
+ .backgroundClip('text')
161
+ .webkitBackgroundClip('text')
162
+ .color('transparent')
163
+ .webkitTextFillColor('transparent')
164
+ }
165
+
166
+ export function registerClassicLogoResponsiveStyles(theme: ThemeMode) {
167
+ styleBuilder
168
+ .select('.classic-logo__glyph', theme)
169
+ .media(mediaMin(BREAKPOINTS.sm))
170
+ .width(resolveLogoVar('icon-size', 'sm'))
171
+ .height(resolveLogoVar('icon-size', 'sm'))
172
+ .minWidth(resolveLogoVar('icon-size', 'sm'))
173
+ .minHeight(resolveLogoVar('icon-size', 'sm'))
174
+
175
+ styleBuilder
176
+ .select('.classic-logo__glyph', theme)
177
+ .media(mediaMin(BREAKPOINTS.md))
178
+ .width(resolveLogoVar('icon-size', 'md'))
179
+ .height(resolveLogoVar('icon-size', 'md'))
180
+ .minWidth(resolveLogoVar('icon-size', 'md'))
181
+ .minHeight(resolveLogoVar('icon-size', 'md'))
182
+
183
+ styleBuilder
184
+ .select('.classic-logo__glyph', theme)
185
+ .media(mediaMin(BREAKPOINTS.lg))
186
+ .width(resolveLogoVar('icon-size', 'lg'))
187
+ .height(resolveLogoVar('icon-size', 'lg'))
188
+ .minWidth(resolveLogoVar('icon-size', 'lg'))
189
+ .minHeight(resolveLogoVar('icon-size', 'lg'))
190
+
191
+ styleBuilder
192
+ .select('.classic-logo__glyph', theme)
193
+ .media(mediaMin(BREAKPOINTS.xl))
194
+ .width(resolveLogoVar('icon-size', 'xl'))
195
+ .height(resolveLogoVar('icon-size', 'xl'))
196
+ .minWidth(resolveLogoVar('icon-size', 'xl'))
197
+ .minHeight(resolveLogoVar('icon-size', 'xl'))
198
+
199
+ styleBuilder
200
+ .select('.classic-logo__brand', theme)
201
+ .media(mediaMin(BREAKPOINTS.sm))
202
+ .fontSize(resolveLogoVar('brand-size', 'sm'))
203
+
204
+ styleBuilder
205
+ .select('.classic-logo__brand', theme)
206
+ .media(mediaMin(BREAKPOINTS.md))
207
+ .fontSize(resolveLogoVar('brand-size', 'md'))
208
+
209
+ styleBuilder
210
+ .select('.classic-logo__brand', theme)
211
+ .media(mediaMin(BREAKPOINTS.lg))
212
+ .fontSize(resolveLogoVar('brand-size', 'lg'))
213
+
214
+ styleBuilder
215
+ .select('.classic-logo__brand', theme)
216
+ .media(mediaMin(BREAKPOINTS.xl))
217
+ .fontSize(resolveLogoVar('brand-size', 'xl'))
218
+
219
+ styleBuilder
220
+ .select('.classic-logo__subtitle', theme)
221
+ .media(mediaMin(BREAKPOINTS.sm))
222
+ .fontSize(resolveLogoVar('subtitle-size', 'sm'))
223
+ .marginLeft(resolveLogoVar('subtitle-inset', 'sm'))
224
+ .marginRight(resolveLogoVar('subtitle-inset', 'sm'))
225
+
226
+ styleBuilder
227
+ .select('.classic-logo__subtitle', theme)
228
+ .media(mediaMin(BREAKPOINTS.md))
229
+ .fontSize(resolveLogoVar('subtitle-size', 'md'))
230
+ .marginLeft(resolveLogoVar('subtitle-inset', 'md'))
231
+ .marginRight(resolveLogoVar('subtitle-inset', 'md'))
232
+
233
+ styleBuilder
234
+ .select('.classic-logo__subtitle', theme)
235
+ .media(mediaMin(BREAKPOINTS.lg))
236
+ .fontSize(resolveLogoVar('subtitle-size', 'lg'))
237
+ .marginLeft(resolveLogoVar('subtitle-inset', 'lg'))
238
+ .marginRight(resolveLogoVar('subtitle-inset', 'lg'))
239
+
240
+ styleBuilder
241
+ .select('.classic-logo__subtitle', theme)
242
+ .media(mediaMin(BREAKPOINTS.xl))
243
+ .fontSize(resolveLogoVar('subtitle-size', 'xl'))
244
+ .marginLeft(resolveLogoVar('subtitle-inset', 'xl'))
245
+ .marginRight(resolveLogoVar('subtitle-inset', 'xl'))
246
+ }
247
+
248
+ function resolveLogoVar(
249
+ token: LogoResponsiveToken,
250
+ breakpoint?: LogoBreakpoint,
251
+ ): string {
252
+ if (!breakpoint) {
253
+ return `var(--ps-classic-logo-${token}, ${getLogoDefaultValue(token, 'base')})`
254
+ }
255
+
256
+ return `var(--ps-classic-logo-${token}-${breakpoint}, ${getLogoDefaultValue(token, breakpoint)})`
257
+ }
258
+
259
+ function getLogoDefaultValue(
260
+ token: LogoResponsiveToken,
261
+ breakpoint: 'base' | LogoBreakpoint,
262
+ ): string {
263
+ switch (token) {
264
+ case 'brand-size':
265
+ return LOGO_DEFAULTS.brandSize[breakpoint]
266
+ case 'subtitle-size':
267
+ return LOGO_DEFAULTS.subtitleSize[breakpoint]
268
+ case 'icon-size':
269
+ return LOGO_DEFAULTS.iconSize[breakpoint]
270
+ case 'subtitle-inset':
271
+ return LOGO_DEFAULTS.subtitleInset[breakpoint]
272
+ }
273
+ }
274
+
275
+ export function registerClassicLogoInteractiveStyles(
276
+ theme: ThemeMode,
277
+ palette: ThemePalette,
278
+ ) {
279
+ styleBuilder
280
+ .select('.classic-logo__link[href]', theme)
281
+ .cursor('pointer')
282
+ .transition('border-color 180ms ease')
283
+
284
+ styleBuilder
285
+ .select('.classic-logo__link[href]:hover', theme)
286
+ .borderColor(palette.current.border.default)
287
+
288
+ styleBuilder
289
+ .select('.classic-logo__link[href]:focus-visible', theme)
290
+ .outline(`2px solid ${palette.current.border.focus}`)
291
+ .outlineOffset('0')
292
+ }