@ngxsmk/theme 1.3.2 → 3.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.
- package/README.md +13 -1
- package/esm2022/lib/css.mjs +128 -30
- package/esm2022/lib/ionic-provider.mjs +30 -0
- package/esm2022/lib/theme-injector.service.mjs +92 -0
- package/esm2022/lib/theme.service.mjs +19 -20
- package/esm2022/lib/token-adapter.mjs +185 -0
- package/esm2022/lib/tokens.mjs +25 -2
- package/esm2022/public-api.mjs +4 -1
- package/fesm2022/ngxsmk-theme.mjs +466 -49
- package/fesm2022/ngxsmk-theme.mjs.map +1 -1
- package/lib/css.d.ts.map +1 -1
- package/lib/ionic-provider.d.ts +18 -0
- package/lib/ionic-provider.d.ts.map +1 -0
- package/lib/theme-injector.service.d.ts +37 -0
- package/lib/theme-injector.service.d.ts.map +1 -0
- package/lib/theme.service.d.ts +12 -6
- package/lib/theme.service.d.ts.map +1 -1
- package/lib/token-adapter.d.ts +28 -0
- package/lib/token-adapter.d.ts.map +1 -0
- package/lib/tokens.d.ts +13 -0
- package/lib/tokens.d.ts.map +1 -1
- package/package.json +34 -14
- package/public-api.d.ts +3 -0
- package/public-api.d.ts.map +1 -1
- package/styles/ngxsmk.css +77 -1
- package/styles/ngxsmk.emerald.css +77 -1
- package/styles/ngxsmk.neutral.css +77 -1
- package/styles/ngxsmk.rose.css +77 -1
- package/styles/ngxsmk.violet.css +77 -1
- package/tailwind-v4.css +234 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DOCUMENT } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, signal, computed, effect,
|
|
3
|
+
import { inject, Injectable, signal, computed, effect, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Design token model for the NGXSMK universal theme engine.
|
|
@@ -148,7 +148,7 @@ const DEFAULT_SEMANTIC = {
|
|
|
148
148
|
};
|
|
149
149
|
const DEFAULT_FONT_FAMILY = {
|
|
150
150
|
sans: "'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif",
|
|
151
|
-
mono: "'
|
|
151
|
+
mono: "'Geist Mono', 'Geist Mono Fallback', monospace",
|
|
152
152
|
};
|
|
153
153
|
const DEFAULT_TYPOGRAPHY = {
|
|
154
154
|
display: {
|
|
@@ -213,6 +213,20 @@ const TRACKING = {
|
|
|
213
213
|
normal: '0',
|
|
214
214
|
wide: '0.08em',
|
|
215
215
|
};
|
|
216
|
+
/** Font-weight tokens — standalone scale for direct use outside typography presets. */
|
|
217
|
+
const FONT_WEIGHT = {
|
|
218
|
+
regular: '400',
|
|
219
|
+
medium: '500',
|
|
220
|
+
semibold: '600',
|
|
221
|
+
bold: '700',
|
|
222
|
+
};
|
|
223
|
+
/** Line-height tokens — unitless multipliers for flexible leading control. */
|
|
224
|
+
const LEADING = {
|
|
225
|
+
none: '1',
|
|
226
|
+
tight: '1.25',
|
|
227
|
+
normal: '1.5',
|
|
228
|
+
relaxed: '1.625',
|
|
229
|
+
};
|
|
216
230
|
const SPACING = {
|
|
217
231
|
'0': '0px',
|
|
218
232
|
'0-5': '0.125rem',
|
|
@@ -289,6 +303,15 @@ const EASING = {
|
|
|
289
303
|
'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
290
304
|
emphasized: 'cubic-bezier(0.2, 0, 0, 1)',
|
|
291
305
|
};
|
|
306
|
+
/** Safe-area inset defaults (fallback to CSS env() variables for iOS/Android/Ionic). */
|
|
307
|
+
const SAFE_AREA = {
|
|
308
|
+
top: 'env(safe-area-inset-top, 0px)',
|
|
309
|
+
bottom: 'env(safe-area-inset-bottom, 0px)',
|
|
310
|
+
left: 'env(safe-area-inset-left, 0px)',
|
|
311
|
+
right: 'env(safe-area-inset-right, 0px)',
|
|
312
|
+
};
|
|
313
|
+
/** Minimum touch target size for mobile accessibility (Apple HIG & Android Material). */
|
|
314
|
+
const TOUCH_TARGET_MIN = '44px';
|
|
292
315
|
|
|
293
316
|
/**
|
|
294
317
|
* Identity helper that gives `ngxsmk.config.ts` files full type inference:
|
|
@@ -339,51 +362,34 @@ function scaleVars(prefix, scale) {
|
|
|
339
362
|
}
|
|
340
363
|
return vars;
|
|
341
364
|
}
|
|
342
|
-
/**
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
...scaleVars('brand', theme.brand),
|
|
346
|
-
...scaleVars('secondary', theme.secondary),
|
|
347
|
-
...scaleVars('neutral', theme.neutral),
|
|
348
|
-
'--ngxsmk-font-sans': theme.fontFamily.sans,
|
|
349
|
-
'--ngxsmk-font-mono': theme.fontFamily.mono,
|
|
350
|
-
'--ngxsmk-radius-base': theme.radius[theme.radiusBase],
|
|
351
|
-
'--ngxsmk-control-height': CONTROL_HEIGHT,
|
|
352
|
-
// Motion Tokens
|
|
353
|
-
'--ngxsmk-motion-duration': 'var(--ngxsmk-duration-normal)',
|
|
354
|
-
'--ngxsmk-motion-ease': 'var(--ngxsmk-ease-in-out)',
|
|
355
|
-
// Interactive-state motion: shared lift/press transforms. Neutralized to
|
|
356
|
-
// `none` under prefers-reduced-motion (see buildThemeCss).
|
|
357
|
-
'--ngxsmk-hover-lift': 'translateY(-1px)',
|
|
358
|
-
'--ngxsmk-press-scale': 'scale(0.98)',
|
|
359
|
-
// Focus ring: THE focus indicator for every component. Aliases the
|
|
360
|
-
// mode-aware --ngxsmk-shadow-focus; the error variant derives from the
|
|
361
|
-
// semantic error role so invalid controls ring red in both modes.
|
|
362
|
-
'--ngxsmk-focus-ring': 'var(--ngxsmk-shadow-focus)',
|
|
363
|
-
'--ngxsmk-focus-ring-error': '0 0 0 2px color-mix(in srgb, var(--ngxsmk-color-error) 15%, transparent), ' +
|
|
364
|
-
'0 0 0 4px color-mix(in srgb, var(--ngxsmk-color-error) 45%, transparent)',
|
|
365
|
-
// Component-specific Tokens (Buttons)
|
|
365
|
+
/** Default CSS custom properties for each component, emitted once in :root. */
|
|
366
|
+
const COMPONENT_TOKEN_DEFAULTS = {
|
|
367
|
+
button: {
|
|
366
368
|
'--ngxsmk-button-bg': 'var(--ngxsmk-color-primary)',
|
|
367
369
|
'--ngxsmk-button-color': 'var(--ngxsmk-color-on-primary)',
|
|
368
370
|
'--ngxsmk-button-font-family': 'var(--ngxsmk-font-sans)',
|
|
369
371
|
'--ngxsmk-button-font-weight': '500',
|
|
370
372
|
'--ngxsmk-button-radius': 'var(--ngxsmk-radius-base)',
|
|
371
373
|
'--ngxsmk-button-disabled-opacity': '0.5',
|
|
372
|
-
|
|
374
|
+
},
|
|
375
|
+
card: {
|
|
373
376
|
'--ngxsmk-card-bg': 'var(--ngxsmk-color-surface)',
|
|
374
377
|
'--ngxsmk-card-radius': 'var(--ngxsmk-radius-xl)',
|
|
375
378
|
'--ngxsmk-card-shadow': 'var(--ngxsmk-shadow-sm)',
|
|
376
379
|
'--ngxsmk-card-border-color': 'var(--ngxsmk-color-outline)',
|
|
377
|
-
|
|
380
|
+
},
|
|
381
|
+
accordion: {
|
|
378
382
|
'--ngxsmk-accordion-bg': 'var(--ngxsmk-color-surface)',
|
|
379
383
|
'--ngxsmk-accordion-radius': 'var(--ngxsmk-radius-lg)',
|
|
380
384
|
'--ngxsmk-accordion-border-color': 'var(--ngxsmk-color-outline)',
|
|
381
|
-
|
|
385
|
+
},
|
|
386
|
+
progress: {
|
|
382
387
|
'--ngxsmk-progress-height': '0.5rem',
|
|
383
388
|
'--ngxsmk-progress-bg': 'var(--ngxsmk-color-surface-variant)',
|
|
384
389
|
'--ngxsmk-progress-color': 'var(--ngxsmk-color-primary)',
|
|
385
390
|
'--ngxsmk-progress-radius': 'var(--ngxsmk-radius-full)',
|
|
386
|
-
|
|
391
|
+
},
|
|
392
|
+
switch: {
|
|
387
393
|
'--ngxsmk-switch-width': '2.25rem',
|
|
388
394
|
'--ngxsmk-switch-height': '1.25rem',
|
|
389
395
|
'--ngxsmk-switch-radius': 'var(--ngxsmk-radius-full)',
|
|
@@ -391,6 +397,80 @@ function staticVars(theme) {
|
|
|
391
397
|
'--ngxsmk-switch-thumb-size': '0.875rem',
|
|
392
398
|
'--ngxsmk-switch-thumb-bg': 'var(--ngxsmk-color-surface)',
|
|
393
399
|
'--ngxsmk-switch-checked-bg': 'var(--ngxsmk-color-primary)',
|
|
400
|
+
},
|
|
401
|
+
spreadsheet: {
|
|
402
|
+
'--ngxsmk-spreadsheet-bg': 'var(--ngxsmk-color-surface)',
|
|
403
|
+
'--ngxsmk-spreadsheet-header-bg': 'var(--ngxsmk-color-surface-variant)',
|
|
404
|
+
'--ngxsmk-spreadsheet-header-color': 'var(--ngxsmk-color-on-surface-variant)',
|
|
405
|
+
'--ngxsmk-spreadsheet-border': 'var(--ngxsmk-color-outline)',
|
|
406
|
+
'--ngxsmk-spreadsheet-grid-color': 'var(--ngxsmk-color-outline-variant)',
|
|
407
|
+
'--ngxsmk-spreadsheet-hover-bg': 'var(--ngxsmk-color-surface-hover)',
|
|
408
|
+
'--ngxsmk-spreadsheet-selected-bg': 'color-mix(in srgb, var(--ngxsmk-color-primary) 12%, transparent)',
|
|
409
|
+
'--ngxsmk-spreadsheet-active-border': 'var(--ngxsmk-color-primary)',
|
|
410
|
+
'--ngxsmk-spreadsheet-font': 'var(--ngxsmk-font-sans)',
|
|
411
|
+
'--ngxsmk-spreadsheet-font-mono': 'var(--ngxsmk-font-mono)',
|
|
412
|
+
'--ngxsmk-spreadsheet-row-height': '2.25rem',
|
|
413
|
+
'--ngxsmk-spreadsheet-header-height': '2.5rem',
|
|
414
|
+
'--ngxsmk-spreadsheet-cell-padding': '6px 10px',
|
|
415
|
+
'--ngxsmk-spreadsheet-cell-font-size': 'var(--ngxsmk-text-body-sm-size)',
|
|
416
|
+
'--ngxsmk-spreadsheet-scrollbar-size': '8px',
|
|
417
|
+
'--ngxsmk-spreadsheet-shadow': 'var(--ngxsmk-shadow-sm)',
|
|
418
|
+
'--ngxsmk-spreadsheet-radius': 'var(--ngxsmk-radius-lg)',
|
|
419
|
+
'--ngxsmk-spreadsheet-edit-bg': 'var(--ngxsmk-color-surface)',
|
|
420
|
+
'--ngxsmk-spreadsheet-edit-border': 'var(--ngxsmk-color-primary)',
|
|
421
|
+
'--ngxsmk-spreadsheet-row-hover-bg': 'var(--ngxsmk-color-surface-hover)',
|
|
422
|
+
'--ngxsmk-spreadsheet-col-hover-bg': 'color-mix(in srgb, var(--ngxsmk-color-primary) 6%, transparent)',
|
|
423
|
+
'--ngxsmk-spreadsheet-frozen-shadow': '2px 0 4px rgba(0,0,0,0.08)',
|
|
424
|
+
'--ngxsmk-spreadsheet-selection-range': 'color-mix(in srgb, var(--ngxsmk-color-primary) 8%, transparent)',
|
|
425
|
+
},
|
|
426
|
+
inputGroup: {
|
|
427
|
+
'--ngxsmk-input-group-bg': 'var(--ngxsmk-color-surface)',
|
|
428
|
+
'--ngxsmk-input-group-border': 'var(--ngxsmk-color-outline-strong)',
|
|
429
|
+
'--ngxsmk-input-group-radius': 'var(--ngxsmk-radius-md)',
|
|
430
|
+
'--ngxsmk-input-group-shadow': 'none',
|
|
431
|
+
'--ngxsmk-input-group-focus-border': 'var(--ngxsmk-color-primary)',
|
|
432
|
+
'--ngxsmk-input-group-focus-shadow': '0 0 0 3px color-mix(in srgb, var(--ngxsmk-color-primary) 15%, transparent)',
|
|
433
|
+
'--ngxsmk-input-group-error-border': 'var(--ngxsmk-color-error)',
|
|
434
|
+
'--ngxsmk-input-group-error-focus-shadow': '0 0 0 3px color-mix(in srgb, var(--ngxsmk-color-error) 15%, transparent)',
|
|
435
|
+
'--ngxsmk-input-group-success-border': 'var(--ngxsmk-color-success)',
|
|
436
|
+
'--ngxsmk-input-group-warning-border': 'var(--ngxsmk-color-warning)',
|
|
437
|
+
'--ngxsmk-input-group-height': '2.5rem',
|
|
438
|
+
'--ngxsmk-input-group-padding': '0.75rem',
|
|
439
|
+
'--ngxsmk-input-group-gap': 'var(--ngxsmk-space-2)',
|
|
440
|
+
'--ngxsmk-input-group-font': 'var(--ngxsmk-font-sans)',
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
/** Tokens that do not change between light and dark mode. */
|
|
444
|
+
function staticVars(theme) {
|
|
445
|
+
const vars = {
|
|
446
|
+
...scaleVars('brand', theme.brand),
|
|
447
|
+
...scaleVars('secondary', theme.secondary),
|
|
448
|
+
...scaleVars('neutral', theme.neutral),
|
|
449
|
+
'--ngxsmk-font-sans': theme.fontFamily.sans,
|
|
450
|
+
'--ngxsmk-font-mono': theme.fontFamily.mono,
|
|
451
|
+
'--ngxsmk-radius-base': theme.radius[theme.radiusBase],
|
|
452
|
+
'--ngxsmk-control-height': CONTROL_HEIGHT,
|
|
453
|
+
// Motion Tokens
|
|
454
|
+
'--ngxsmk-motion-duration': 'var(--ngxsmk-duration-normal)',
|
|
455
|
+
'--ngxsmk-motion-ease': 'var(--ngxsmk-ease-in-out)',
|
|
456
|
+
// Interactive-state motion: shared lift/press transforms. Neutralized to
|
|
457
|
+
// `none` under prefers-reduced-motion (see buildThemeCss).
|
|
458
|
+
'--ngxsmk-hover-lift': 'translateY(-1px)',
|
|
459
|
+
'--ngxsmk-press-scale': 'scale(0.98)',
|
|
460
|
+
// Focus ring: THE focus indicator for every component. Aliases the
|
|
461
|
+
// mode-aware --ngxsmk-shadow-focus; the error variant derives from the
|
|
462
|
+
// semantic error role so invalid controls ring red in both modes.
|
|
463
|
+
'--ngxsmk-focus-ring': 'var(--ngxsmk-shadow-focus)',
|
|
464
|
+
'--ngxsmk-focus-ring-error': '0 0 0 2px color-mix(in srgb, var(--ngxsmk-color-error) 15%, transparent), ' +
|
|
465
|
+
'0 0 0 4px color-mix(in srgb, var(--ngxsmk-color-error) 45%, transparent)',
|
|
466
|
+
// Mobile viewport & safe-area tokens (iOS / Android / Ionic)
|
|
467
|
+
'--ngxsmk-safe-area-top': 'env(safe-area-inset-top, 0px)',
|
|
468
|
+
'--ngxsmk-safe-area-bottom': 'env(safe-area-inset-bottom, 0px)',
|
|
469
|
+
'--ngxsmk-safe-area-left': 'env(safe-area-inset-left, 0px)',
|
|
470
|
+
'--ngxsmk-safe-area-right': 'env(safe-area-inset-right, 0px)',
|
|
471
|
+
'--ngxsmk-touch-target-min': '44px',
|
|
472
|
+
// Component-specific token defaults
|
|
473
|
+
...Object.values(COMPONENT_TOKEN_DEFAULTS).reduce((merged, tokens) => ({ ...merged, ...tokens }), {}),
|
|
394
474
|
};
|
|
395
475
|
for (const [key, value] of Object.entries(CONTROL_HEIGHTS)) {
|
|
396
476
|
vars[`--ngxsmk-control-height-${key}`] = value;
|
|
@@ -404,6 +484,12 @@ function staticVars(theme) {
|
|
|
404
484
|
for (const [key, value] of Object.entries(TRACKING)) {
|
|
405
485
|
vars[`--ngxsmk-tracking-${key}`] = value;
|
|
406
486
|
}
|
|
487
|
+
for (const [key, value] of Object.entries(FONT_WEIGHT)) {
|
|
488
|
+
vars[`--ngxsmk-font-weight-${key}`] = value;
|
|
489
|
+
}
|
|
490
|
+
for (const [key, value] of Object.entries(LEADING)) {
|
|
491
|
+
vars[`--ngxsmk-leading-${key}`] = value;
|
|
492
|
+
}
|
|
407
493
|
for (const [key, value] of Object.entries(theme.spacing)) {
|
|
408
494
|
vars[`--ngxsmk-space-${key}`] = value;
|
|
409
495
|
}
|
|
@@ -461,12 +547,17 @@ function lightVars(theme) {
|
|
|
461
547
|
'--ngxsmk-color-on-surface': '#0A1317',
|
|
462
548
|
'--ngxsmk-color-surface-variant': theme.neutral[100],
|
|
463
549
|
'--ngxsmk-color-on-surface-variant': theme.neutral[500],
|
|
550
|
+
'--ngxsmk-color-surface-muted': theme.neutral[50],
|
|
551
|
+
'--ngxsmk-color-surface-elevated': '#FFFFFF',
|
|
552
|
+
'--ngxsmk-color-surface-overlay': '#FFFFFF',
|
|
553
|
+
'--ngxsmk-color-on-surface-muted': theme.neutral[400],
|
|
464
554
|
'--ngxsmk-color-surface-hover': 'rgb(5 54 89 / 0.05)',
|
|
465
555
|
'--ngxsmk-color-surface-active': 'rgb(5 54 89 / 0.1)',
|
|
466
556
|
'--ngxsmk-color-surface-container': '#FFFFFF',
|
|
467
557
|
'--ngxsmk-color-outline': 'rgb(5 54 89 / 0.1)',
|
|
468
558
|
'--ngxsmk-color-outline-strong': theme.neutral[200],
|
|
469
559
|
'--ngxsmk-color-outline-variant': 'rgb(5 54 89 / 0.06)',
|
|
560
|
+
'--ngxsmk-color-outline-subtle': 'rgb(5 54 89 / 0.05)',
|
|
470
561
|
// Elevation ladder
|
|
471
562
|
'--ngxsmk-color-surface-1': '#FFFFFF',
|
|
472
563
|
'--ngxsmk-color-surface-2': theme.neutral[50],
|
|
@@ -476,6 +567,19 @@ function lightVars(theme) {
|
|
|
476
567
|
'--ngxsmk-color-ring': theme.brandBase,
|
|
477
568
|
'--ngxsmk-color-backdrop': 'rgb(1 18 40 / 0.4)',
|
|
478
569
|
'--ngxsmk-shadow-focus': `0 0 0 2px ${theme.brand[100]}, 0 0 0 4px color-mix(in srgb, ${theme.brandBase} 45%, transparent)`,
|
|
570
|
+
// Categorical chart palette — a fixed 8-hue qualitative set (NOT derived
|
|
571
|
+
// from the brand, so adjacent series stay distinguishable across themes),
|
|
572
|
+
// stepped for the light surface. Validated for the light chart surface:
|
|
573
|
+
// CVD worst-adjacent ΔE 24.2, chroma/lightness in-band. Charts read these
|
|
574
|
+
// via getComputedStyle; consumers can override any slot to rebrand series.
|
|
575
|
+
'--ngxsmk-chart-1': '#2A78D6',
|
|
576
|
+
'--ngxsmk-chart-2': '#1BAF7A',
|
|
577
|
+
'--ngxsmk-chart-3': '#EDA100',
|
|
578
|
+
'--ngxsmk-chart-4': '#008300',
|
|
579
|
+
'--ngxsmk-chart-5': '#4A3AA7',
|
|
580
|
+
'--ngxsmk-chart-6': '#E34948',
|
|
581
|
+
'--ngxsmk-chart-7': '#E87BA4',
|
|
582
|
+
'--ngxsmk-chart-8': '#EB6834',
|
|
479
583
|
};
|
|
480
584
|
const tertiary = deriveScale(rotateHue(theme.brandBase, 64));
|
|
481
585
|
vars['--ngxsmk-color-tertiary'] = tertiary[500];
|
|
@@ -515,12 +619,17 @@ function darkVars(theme) {
|
|
|
515
619
|
'--ngxsmk-color-on-surface': '#DFE2E5',
|
|
516
620
|
'--ngxsmk-color-surface-variant': '#28292C',
|
|
517
621
|
'--ngxsmk-color-on-surface-variant': '#AAAFB5',
|
|
622
|
+
'--ngxsmk-color-surface-muted': '#141416',
|
|
623
|
+
'--ngxsmk-color-surface-elevated': '#28292C',
|
|
624
|
+
'--ngxsmk-color-surface-overlay': '#2F3136',
|
|
625
|
+
'--ngxsmk-color-on-surface-muted': '#777C85',
|
|
518
626
|
'--ngxsmk-color-surface-hover': 'rgb(255 255 255 / 0.05)',
|
|
519
627
|
'--ngxsmk-color-surface-active': 'rgb(255 255 255 / 0.1)',
|
|
520
628
|
'--ngxsmk-color-surface-container': '#28292C',
|
|
521
629
|
'--ngxsmk-color-outline': 'rgb(242 244 246 / 0.1)',
|
|
522
630
|
'--ngxsmk-color-outline-strong': '#494D53',
|
|
523
631
|
'--ngxsmk-color-outline-variant': 'rgb(242 244 246 / 0.06)',
|
|
632
|
+
'--ngxsmk-color-outline-subtle': 'rgb(242 244 246 / 0.05)',
|
|
524
633
|
// Elevation ladder
|
|
525
634
|
'--ngxsmk-color-surface-1': theme.neutral[800],
|
|
526
635
|
'--ngxsmk-color-surface-2': theme.neutral[700],
|
|
@@ -530,6 +639,18 @@ function darkVars(theme) {
|
|
|
530
639
|
'--ngxsmk-color-ring': theme.brand[400],
|
|
531
640
|
'--ngxsmk-color-backdrop': 'rgb(17 17 18 / 0.6)',
|
|
532
641
|
'--ngxsmk-shadow-focus': `0 0 0 2px color-mix(in srgb, ${theme.brand[400]} 25%, transparent), 0 0 0 4px color-mix(in srgb, ${theme.brand[400]} 45%, transparent)`,
|
|
642
|
+
// Categorical chart palette — the same eight hues as light, re-stepped for
|
|
643
|
+
// the dark surface (not a different palette). Validated for the dark chart
|
|
644
|
+
// surface: all slots ≥ 3:1 contrast, CVD worst-adjacent ΔE 10.3 (floor band,
|
|
645
|
+
// relies on the legend/labels charts already render for secondary encoding).
|
|
646
|
+
'--ngxsmk-chart-1': '#3987E5',
|
|
647
|
+
'--ngxsmk-chart-2': '#199E70',
|
|
648
|
+
'--ngxsmk-chart-3': '#C98500',
|
|
649
|
+
'--ngxsmk-chart-4': '#008300',
|
|
650
|
+
'--ngxsmk-chart-5': '#9085E9',
|
|
651
|
+
'--ngxsmk-chart-6': '#E66767',
|
|
652
|
+
'--ngxsmk-chart-7': '#D55181',
|
|
653
|
+
'--ngxsmk-chart-8': '#D95926',
|
|
533
654
|
};
|
|
534
655
|
const tertiary = deriveScale(rotateHue(theme.brandBase, 64));
|
|
535
656
|
vars['--ngxsmk-color-tertiary'] = tertiary[400];
|
|
@@ -658,19 +779,290 @@ const presets = {
|
|
|
658
779
|
rose: rosePreset,
|
|
659
780
|
};
|
|
660
781
|
|
|
661
|
-
|
|
782
|
+
/**
|
|
783
|
+
* Maps NGXSMK semantic color roles to Ionic 7 & 8 color variable specifications.
|
|
784
|
+
*
|
|
785
|
+
* Emits full color suites (base, rgb, contrast, contrast-rgb, shade, tint),
|
|
786
|
+
* 19-step contrast ladder (--ion-color-step-50..950), safe area mappings, and
|
|
787
|
+
* surface variables for seamless Ionic framework interoperability.
|
|
788
|
+
*/
|
|
789
|
+
const ionicVarsAdapter = {
|
|
790
|
+
name: 'ionic',
|
|
791
|
+
vars(theme, options) {
|
|
792
|
+
if (options?.isDark) {
|
|
793
|
+
return this.darkVars ? this.darkVars(theme) : buildLightVars(theme);
|
|
794
|
+
}
|
|
795
|
+
return buildLightVars(theme);
|
|
796
|
+
},
|
|
797
|
+
darkVars(theme) {
|
|
798
|
+
return buildDarkVars(theme);
|
|
799
|
+
},
|
|
800
|
+
};
|
|
801
|
+
function buildColorSuite(prefix, baseHex, vars) {
|
|
802
|
+
const contrastHex = getContrastHex(baseHex);
|
|
803
|
+
vars[`--ion-color-${prefix}`] = baseHex;
|
|
804
|
+
vars[`--ion-color-${prefix}-rgb`] = hexToRgbStr(baseHex);
|
|
805
|
+
vars[`--ion-color-${prefix}-contrast`] = contrastHex;
|
|
806
|
+
vars[`--ion-color-${prefix}-contrast-rgb`] = hexToRgbStr(contrastHex);
|
|
807
|
+
vars[`--ion-color-${prefix}-shade`] = shiftLightnessHex(baseHex, -12);
|
|
808
|
+
vars[`--ion-color-${prefix}-tint`] = shiftLightnessHex(baseHex, 10);
|
|
809
|
+
}
|
|
810
|
+
function buildLightVars(theme) {
|
|
811
|
+
const vars = {};
|
|
812
|
+
// Primary & Secondary
|
|
813
|
+
buildColorSuite('primary', theme.brandBase, vars);
|
|
814
|
+
buildColorSuite('secondary', theme.secondaryBase, vars);
|
|
815
|
+
// Tertiary (derived from brand hue + 64)
|
|
816
|
+
const tertiary = deriveScale(rotateHue(theme.brandBase, 64));
|
|
817
|
+
buildColorSuite('tertiary', tertiary[500], vars);
|
|
818
|
+
// Status & Feedback colors
|
|
819
|
+
const successHex = theme.semantic['success'] ?? '#0D8626';
|
|
820
|
+
const warningHex = theme.semantic['warning'] ?? '#E9AF08';
|
|
821
|
+
const dangerHex = theme.semantic['error'] ?? '#E3193B';
|
|
822
|
+
const infoHex = theme.semantic['info'] ?? '#2563EB';
|
|
823
|
+
buildColorSuite('success', successHex, vars);
|
|
824
|
+
buildColorSuite('warning', warningHex, vars);
|
|
825
|
+
buildColorSuite('danger', dangerHex, vars);
|
|
826
|
+
buildColorSuite('info', infoHex, vars);
|
|
827
|
+
// Medium / Light / Dark baseline scales
|
|
828
|
+
buildColorSuite('light', theme.neutral[100] ?? '#F1F4F7', vars);
|
|
829
|
+
buildColorSuite('medium', theme.neutral[500] ?? '#778593', vars);
|
|
830
|
+
buildColorSuite('dark', theme.neutral[900] ?? '#18181A', vars);
|
|
831
|
+
// Background / Surfaces
|
|
832
|
+
const bgHex = theme.neutral[50] ?? '#FFFFFF';
|
|
833
|
+
const textHex = '#0A1317';
|
|
834
|
+
vars['--ion-background-color'] = bgHex;
|
|
835
|
+
vars['--ion-background-color-rgb'] = hexToRgbStr(bgHex);
|
|
836
|
+
vars['--ion-text-color'] = textHex;
|
|
837
|
+
vars['--ion-text-color-rgb'] = hexToRgbStr(textHex);
|
|
838
|
+
vars['--ion-card-background'] = '#FFFFFF';
|
|
839
|
+
vars['--ion-item-background'] = '#FFFFFF';
|
|
840
|
+
vars['--ion-item-color'] = textHex;
|
|
841
|
+
vars['--ion-toolbar-background'] = bgHex;
|
|
842
|
+
vars['--ion-toolbar-color'] = textHex;
|
|
843
|
+
vars['--ion-tab-bar-background'] = '#FFFFFF';
|
|
844
|
+
vars['--ion-border-color'] = theme.neutral[200] ?? '#CCD3DB';
|
|
845
|
+
// Typography & Radius
|
|
846
|
+
vars['--ion-font-family'] = theme.fontFamily.sans;
|
|
847
|
+
vars['--ion-border-radius'] = theme.radius[theme.radiusBase];
|
|
848
|
+
// Stepped colors for light mode (interpolated between bg and text)
|
|
849
|
+
interpolateSteppedColors(bgHex, textHex, vars);
|
|
850
|
+
// Safe area insets
|
|
851
|
+
vars['--ion-safe-area-top'] = 'var(--ngxsmk-safe-area-top, env(safe-area-inset-top, 0px))';
|
|
852
|
+
vars['--ion-safe-area-bottom'] =
|
|
853
|
+
'var(--ngxsmk-safe-area-bottom, env(safe-area-inset-bottom, 0px))';
|
|
854
|
+
vars['--ion-safe-area-left'] = 'var(--ngxsmk-safe-area-left, env(safe-area-inset-left, 0px))';
|
|
855
|
+
vars['--ion-safe-area-right'] = 'var(--ngxsmk-safe-area-right, env(safe-area-inset-right, 0px))';
|
|
856
|
+
return vars;
|
|
857
|
+
}
|
|
858
|
+
function buildDarkVars(theme) {
|
|
859
|
+
const vars = {};
|
|
860
|
+
// Primary & Secondary dark adjustments
|
|
861
|
+
const primaryDark = theme.brand[400] ?? theme.brandBase;
|
|
862
|
+
const secondaryDark = theme.secondary[400] ?? theme.secondaryBase;
|
|
863
|
+
buildColorSuite('primary', primaryDark, vars);
|
|
864
|
+
buildColorSuite('secondary', secondaryDark, vars);
|
|
865
|
+
// Tertiary
|
|
866
|
+
const tertiary = deriveScale(rotateHue(theme.brandBase, 64));
|
|
867
|
+
buildColorSuite('tertiary', tertiary[400] ?? tertiary[500], vars);
|
|
868
|
+
// Status & Feedback
|
|
869
|
+
buildColorSuite('success', shiftLightnessHex(theme.semantic['success'] ?? '#0D8626', 10), vars);
|
|
870
|
+
buildColorSuite('warning', shiftLightnessHex(theme.semantic['warning'] ?? '#E9AF08', 8), vars);
|
|
871
|
+
buildColorSuite('danger', shiftLightnessHex(theme.semantic['error'] ?? '#E3193B', 10), vars);
|
|
872
|
+
buildColorSuite('info', shiftLightnessHex(theme.semantic['info'] ?? '#2563EB', 10), vars);
|
|
873
|
+
buildColorSuite('light', theme.neutral[800] ?? '#1F1F22', vars);
|
|
874
|
+
buildColorSuite('medium', theme.neutral[400] ?? '#778593', vars);
|
|
875
|
+
buildColorSuite('dark', theme.neutral[100] ?? '#F1F4F7', vars);
|
|
876
|
+
// Dark backgrounds & surfaces
|
|
877
|
+
const darkBgHex = '#111112';
|
|
878
|
+
const darkTextHex = '#DFE2E5';
|
|
879
|
+
const darkSurface = '#1F1F22';
|
|
880
|
+
vars['--ion-background-color'] = darkBgHex;
|
|
881
|
+
vars['--ion-background-color-rgb'] = hexToRgbStr(darkBgHex);
|
|
882
|
+
vars['--ion-text-color'] = darkTextHex;
|
|
883
|
+
vars['--ion-text-color-rgb'] = hexToRgbStr(darkTextHex);
|
|
884
|
+
vars['--ion-card-background'] = darkSurface;
|
|
885
|
+
vars['--ion-item-background'] = darkSurface;
|
|
886
|
+
vars['--ion-item-color'] = darkTextHex;
|
|
887
|
+
vars['--ion-toolbar-background'] = '#18181A';
|
|
888
|
+
vars['--ion-toolbar-color'] = darkTextHex;
|
|
889
|
+
vars['--ion-tab-bar-background'] = '#18181A';
|
|
890
|
+
vars['--ion-border-color'] = 'rgba(242, 244, 246, 0.1)';
|
|
891
|
+
// Typography & Radius
|
|
892
|
+
vars['--ion-font-family'] = theme.fontFamily.sans;
|
|
893
|
+
vars['--ion-border-radius'] = theme.radius[theme.radiusBase];
|
|
894
|
+
// Stepped colors for dark mode
|
|
895
|
+
interpolateSteppedColors(darkBgHex, darkTextHex, vars);
|
|
896
|
+
// Safe area insets
|
|
897
|
+
vars['--ion-safe-area-top'] = 'var(--ngxsmk-safe-area-top, env(safe-area-inset-top, 0px))';
|
|
898
|
+
vars['--ion-safe-area-bottom'] =
|
|
899
|
+
'var(--ngxsmk-safe-area-bottom, env(safe-area-inset-bottom, 0px))';
|
|
900
|
+
vars['--ion-safe-area-left'] = 'var(--ngxsmk-safe-area-left, env(safe-area-inset-left, 0px))';
|
|
901
|
+
vars['--ion-safe-area-right'] = 'var(--ngxsmk-safe-area-right, env(safe-area-inset-right, 0px))';
|
|
902
|
+
return vars;
|
|
903
|
+
}
|
|
904
|
+
/** Default NGXSMK adapter — emits `--ngxsmk-*` variables. */
|
|
905
|
+
const cssVarsAdapter = {
|
|
906
|
+
name: 'ngxsmk',
|
|
907
|
+
vars: () => ({}), // Not used directly — buildThemeCss handles this
|
|
908
|
+
};
|
|
909
|
+
// ─── Helpers ──────────────────────────────────────────────
|
|
910
|
+
function hexToRgbStr(hex) {
|
|
911
|
+
const clean = hex.replace('#', '');
|
|
912
|
+
const r = parseInt(clean.substring(0, 2), 16) || 0;
|
|
913
|
+
const g = parseInt(clean.substring(2, 4), 16) || 0;
|
|
914
|
+
const b = parseInt(clean.substring(4, 6), 16) || 0;
|
|
915
|
+
return `${r}, ${g}, ${b}`;
|
|
916
|
+
}
|
|
917
|
+
function getContrastHex(hex) {
|
|
918
|
+
const clean = hex.replace('#', '');
|
|
919
|
+
const r = parseInt(clean.substring(0, 2), 16) || 0;
|
|
920
|
+
const g = parseInt(clean.substring(2, 4), 16) || 0;
|
|
921
|
+
const b = parseInt(clean.substring(4, 6), 16) || 0;
|
|
922
|
+
// Standard WCAG relative luminance approximation
|
|
923
|
+
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
|
924
|
+
return yiq >= 150 ? '#000000' : '#FFFFFF';
|
|
925
|
+
}
|
|
926
|
+
function shiftLightnessHex(hex, amount) {
|
|
927
|
+
const clean = hex.replace('#', '');
|
|
928
|
+
let r = parseInt(clean.substring(0, 2), 16) || 0;
|
|
929
|
+
let g = parseInt(clean.substring(2, 4), 16) || 0;
|
|
930
|
+
let b = parseInt(clean.substring(4, 6), 16) || 0;
|
|
931
|
+
if (amount > 0) {
|
|
932
|
+
r = Math.min(255, r + Math.round((255 - r) * (amount / 100)));
|
|
933
|
+
g = Math.min(255, g + Math.round((255 - g) * (amount / 100)));
|
|
934
|
+
b = Math.min(255, b + Math.round((255 - b) * (amount / 100)));
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
const factor = 1 + amount / 100;
|
|
938
|
+
r = Math.max(0, Math.round(r * factor));
|
|
939
|
+
g = Math.max(0, Math.round(g * factor));
|
|
940
|
+
b = Math.max(0, Math.round(b * factor));
|
|
941
|
+
}
|
|
942
|
+
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`.toUpperCase();
|
|
943
|
+
}
|
|
944
|
+
function interpolateSteppedColors(bgHex, textHex, vars) {
|
|
945
|
+
const cleanBg = bgHex.replace('#', '');
|
|
946
|
+
const cleanText = textHex.replace('#', '');
|
|
947
|
+
const bgR = parseInt(cleanBg.substring(0, 2), 16) || 0;
|
|
948
|
+
const bgG = parseInt(cleanBg.substring(2, 4), 16) || 0;
|
|
949
|
+
const bgB = parseInt(cleanBg.substring(4, 6), 16) || 0;
|
|
950
|
+
const textR = parseInt(cleanText.substring(0, 2), 16) || 0;
|
|
951
|
+
const textG = parseInt(cleanText.substring(2, 4), 16) || 0;
|
|
952
|
+
const textB = parseInt(cleanText.substring(4, 6), 16) || 0;
|
|
953
|
+
const steps = [
|
|
954
|
+
50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950,
|
|
955
|
+
];
|
|
956
|
+
for (const step of steps) {
|
|
957
|
+
const weight = step / 1000;
|
|
958
|
+
const r = Math.round(bgR * (1 - weight) + textR * weight);
|
|
959
|
+
const g = Math.round(bgG * (1 - weight) + textG * weight);
|
|
960
|
+
const b = Math.round(bgB * (1 - weight) + textB * weight);
|
|
961
|
+
const hex = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`.toUpperCase();
|
|
962
|
+
vars[`--ion-color-step-${step}`] = hex;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
662
966
|
const STYLE_ID = 'ngxsmk-theme';
|
|
967
|
+
const IONIC_STYLE_ID = 'ngxsmk-ionic-theme';
|
|
968
|
+
/**
|
|
969
|
+
* Injects generated theme CSS into the document at runtime.
|
|
970
|
+
*
|
|
971
|
+
* Decoupled from dark-mode switching so consumers who only need
|
|
972
|
+
* token injection (e.g. the CLI theme-preview) don't pull in
|
|
973
|
+
* class-toggle / localStorage / matchMedia logic.
|
|
974
|
+
*
|
|
975
|
+
* SSR-safe: all DOM access goes through the injected document.
|
|
976
|
+
*/
|
|
977
|
+
class NgxThemeInjectorService {
|
|
978
|
+
constructor() {
|
|
979
|
+
this.document = inject(DOCUMENT);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Apply a theme at runtime. Generates token CSS from the config and swaps
|
|
983
|
+
* it into a dedicated `<style>` element, overriding the build-time theme.
|
|
984
|
+
*
|
|
985
|
+
* @param adapter Optional output adapter. Defaults to the standard NGXSMK
|
|
986
|
+
* `--ngxsmk-*` CSS variables. Pass `ionicVarsAdapter` to also generate
|
|
987
|
+
* `--ion-*` variables for Ionic components.
|
|
988
|
+
*/
|
|
989
|
+
applyTheme(config, adapter) {
|
|
990
|
+
const head = this.document.head;
|
|
991
|
+
let style = this.document.getElementById(STYLE_ID);
|
|
992
|
+
if (!style) {
|
|
993
|
+
style = this.document.createElement('style');
|
|
994
|
+
style.id = STYLE_ID;
|
|
995
|
+
head?.appendChild(style);
|
|
996
|
+
}
|
|
997
|
+
let css = buildThemeCss(config);
|
|
998
|
+
if (adapter) {
|
|
999
|
+
const resolved = resolveTheme(config);
|
|
1000
|
+
const platformVars = adapter.vars(resolved);
|
|
1001
|
+
css += '\n' + this.buildAdapterCss(adapter.name, platformVars);
|
|
1002
|
+
}
|
|
1003
|
+
if (style) {
|
|
1004
|
+
style.textContent = css;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Apply Ionic-specific token variables alongside the standard NGXSMK theme.
|
|
1009
|
+
* Generates `--ion-*` variables derived from the same `ThemeConfig`.
|
|
1010
|
+
*/
|
|
1011
|
+
applyIonicTheme(config) {
|
|
1012
|
+
const head = this.document.head;
|
|
1013
|
+
let style = this.document.getElementById(IONIC_STYLE_ID);
|
|
1014
|
+
if (!style) {
|
|
1015
|
+
style = this.document.createElement('style');
|
|
1016
|
+
style.id = IONIC_STYLE_ID;
|
|
1017
|
+
head?.appendChild(style);
|
|
1018
|
+
}
|
|
1019
|
+
const resolved = resolveTheme(config);
|
|
1020
|
+
const lightVars = ionicVarsAdapter.vars(resolved);
|
|
1021
|
+
const darkVars = ionicVarsAdapter.darkVars ? ionicVarsAdapter.darkVars(resolved) : undefined;
|
|
1022
|
+
if (style) {
|
|
1023
|
+
style.textContent = this.buildAdapterCss('ionic', lightVars, darkVars);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
/** Remove a runtime-applied theme, falling back to the build-time CSS. */
|
|
1027
|
+
clearTheme() {
|
|
1028
|
+
this.document.getElementById(STYLE_ID)?.remove();
|
|
1029
|
+
}
|
|
1030
|
+
/** Remove the Ionic adapter stylesheet. */
|
|
1031
|
+
clearIonicTheme() {
|
|
1032
|
+
this.document.getElementById(IONIC_STYLE_ID)?.remove();
|
|
1033
|
+
}
|
|
1034
|
+
buildAdapterCss(adapterName, vars, darkVars) {
|
|
1035
|
+
const lines = Object.entries(vars).map(([k, v]) => ` ${k}: ${v};`);
|
|
1036
|
+
let result = `/* @ngxsmk/theme — ${adapterName} adapter */\n:root {\n${lines.join('\n')}\n}`;
|
|
1037
|
+
if (darkVars && Object.keys(darkVars).length > 0) {
|
|
1038
|
+
const darkLines = Object.entries(darkVars).map(([k, v]) => ` ${k}: ${v};`);
|
|
1039
|
+
result += `\n\n:root.dark, body.dark, html.dark, [data-theme="dark"] {\n${darkLines.join('\n')}\n}`;
|
|
1040
|
+
result += `\n\n@media (prefers-color-scheme: dark) {\n :root:not(.light) {\n ${darkLines.join('\n ')}\n }\n}`;
|
|
1041
|
+
}
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.0", ngImport: i0, type: NgxThemeInjectorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1045
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.0", ngImport: i0, type: NgxThemeInjectorService, providedIn: 'root' }); }
|
|
1046
|
+
}
|
|
1047
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.0", ngImport: i0, type: NgxThemeInjectorService, decorators: [{
|
|
1048
|
+
type: Injectable,
|
|
1049
|
+
args: [{ providedIn: 'root' }]
|
|
1050
|
+
}] });
|
|
1051
|
+
|
|
1052
|
+
const STORAGE_KEY = 'ngxsmk-theme-mode';
|
|
663
1053
|
/**
|
|
664
1054
|
* Runtime theme control: dark-mode switching (class strategy) and dynamic
|
|
665
1055
|
* theme application by injecting generated token CSS into the document.
|
|
666
1056
|
*
|
|
1057
|
+
* CSS injection is delegated to {@link NgxThemeInjectorService} for
|
|
1058
|
+
* consumers who only need token injection without dark-mode logic.
|
|
1059
|
+
*
|
|
667
1060
|
* SSR-safe: all DOM access goes through the injected document and degrades
|
|
668
1061
|
* gracefully when `window`/`matchMedia` are unavailable.
|
|
669
1062
|
*/
|
|
670
1063
|
class NgxsmkThemeService {
|
|
671
1064
|
constructor() {
|
|
672
|
-
|
|
673
|
-
// can safely access it when the signals are initialised below.
|
|
1065
|
+
this.injector = inject(NgxThemeInjectorService);
|
|
674
1066
|
this.document = inject(DOCUMENT);
|
|
675
1067
|
this.mode = signal(this.restoreMode());
|
|
676
1068
|
this.systemDark = signal(this.matchSystemDark());
|
|
@@ -693,23 +1085,21 @@ class NgxsmkThemeService {
|
|
|
693
1085
|
toggle() {
|
|
694
1086
|
this.setMode(this.isDark() ? 'light' : 'dark');
|
|
695
1087
|
}
|
|
696
|
-
/**
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
*/
|
|
700
|
-
applyTheme(config) {
|
|
701
|
-
const head = this.document.head;
|
|
702
|
-
let style = this.document.getElementById(STYLE_ID);
|
|
703
|
-
if (!style) {
|
|
704
|
-
style = this.document.createElement('style');
|
|
705
|
-
style.id = STYLE_ID;
|
|
706
|
-
head.appendChild(style);
|
|
707
|
-
}
|
|
708
|
-
style.textContent = buildThemeCss(config);
|
|
1088
|
+
/** @see NgxThemeInjectorService.applyTheme */
|
|
1089
|
+
applyTheme(config, adapter) {
|
|
1090
|
+
this.injector.applyTheme(config, adapter);
|
|
709
1091
|
}
|
|
710
|
-
/**
|
|
1092
|
+
/** @see NgxThemeInjectorService.applyIonicTheme */
|
|
1093
|
+
applyIonicTheme(config) {
|
|
1094
|
+
this.injector.applyIonicTheme(config);
|
|
1095
|
+
}
|
|
1096
|
+
/** @see NgxThemeInjectorService.clearTheme */
|
|
711
1097
|
clearTheme() {
|
|
712
|
-
this.
|
|
1098
|
+
this.injector.clearTheme();
|
|
1099
|
+
}
|
|
1100
|
+
/** @see NgxThemeInjectorService.clearIonicTheme */
|
|
1101
|
+
clearIonicTheme() {
|
|
1102
|
+
this.injector.clearIonicTheme();
|
|
713
1103
|
}
|
|
714
1104
|
restoreMode() {
|
|
715
1105
|
try {
|
|
@@ -734,6 +1124,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.0", ngImpor
|
|
|
734
1124
|
args: [{ providedIn: 'root' }]
|
|
735
1125
|
}], ctorParameters: () => [] });
|
|
736
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* Standalone provider for Angular 18-22+ applications that automatically
|
|
1129
|
+
* synchronizes NGXSMK design tokens with Ionic 7/8 CSS variables on startup.
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* ```ts
|
|
1133
|
+
* export const appConfig: ApplicationConfig = {
|
|
1134
|
+
* providers: [
|
|
1135
|
+
* provideIonicAngular(),
|
|
1136
|
+
* provideNgxsmkIonicTheme(),
|
|
1137
|
+
* ]
|
|
1138
|
+
* };
|
|
1139
|
+
* ```
|
|
1140
|
+
*/
|
|
1141
|
+
function provideNgxsmkIonicTheme(config) {
|
|
1142
|
+
return makeEnvironmentProviders([
|
|
1143
|
+
{
|
|
1144
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
1145
|
+
multi: true,
|
|
1146
|
+
useValue: () => {
|
|
1147
|
+
const theme = inject(NgxsmkThemeService);
|
|
1148
|
+
theme.applyIonicTheme(config ?? violetPreset);
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
]);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
737
1154
|
/*
|
|
738
1155
|
* Public API Surface of @ngxsmk/theme
|
|
739
1156
|
*/
|
|
@@ -742,5 +1159,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.0", ngImpor
|
|
|
742
1159
|
* Generated bundle index. Do not edit.
|
|
743
1160
|
*/
|
|
744
1161
|
|
|
745
|
-
export { CONTROL_HEIGHT, CONTROL_HEIGHTS, DEFAULT_FONT_FAMILY, DEFAULT_NEUTRAL, DEFAULT_SEMANTIC, DEFAULT_TYPOGRAPHY, DURATION, EASING, ICON_SIZE, NgxsmkThemeService, OPACITY, RADIUS, SHADOW, SPACING, TRACKING, Z_INDEX, buildThemeCss, defineConfig, deriveScale, emeraldPreset, hexToOklch, neutralPreset, oklchToHex, presets, resolveTheme, rosePreset, rotateHue, shiftLightness, violetPreset };
|
|
1162
|
+
export { CONTROL_HEIGHT, CONTROL_HEIGHTS, DEFAULT_FONT_FAMILY, DEFAULT_NEUTRAL, DEFAULT_SEMANTIC, DEFAULT_TYPOGRAPHY, DURATION, EASING, FONT_WEIGHT, ICON_SIZE, LEADING, NgxThemeInjectorService, NgxsmkThemeService, OPACITY, RADIUS, SAFE_AREA, SHADOW, SPACING, TOUCH_TARGET_MIN, TRACKING, Z_INDEX, buildThemeCss, cssVarsAdapter, defineConfig, deriveScale, emeraldPreset, hexToOklch, ionicVarsAdapter, neutralPreset, oklchToHex, presets, provideNgxsmkIonicTheme, resolveTheme, rosePreset, rotateHue, shiftLightness, violetPreset };
|
|
746
1163
|
//# sourceMappingURL=ngxsmk-theme.mjs.map
|