@keepui/ui 0.1.7 → 0.2.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.
@@ -123,7 +123,7 @@ class ButtonComponent {
123
123
  'min-h-[2.75rem] text-sm font-medium cursor-pointer select-none',
124
124
  'transition-colors duration-200',
125
125
  'focus-visible:outline-none focus-visible:ring-2',
126
- 'focus-visible:ring-keepui-primary focus-visible:ring-offset-2',
126
+ 'focus-visible:ring-ku-primary-border focus-visible:ring-offset-2',
127
127
  'disabled:opacity-50 disabled:cursor-not-allowed',
128
128
  ].join(' ');
129
129
  const sizeClass = this.fullWidth()
@@ -137,25 +137,25 @@ class ButtonComponent {
137
137
  };
138
138
  const variantMap = {
139
139
  primary: [
140
- 'bg-keepui-primary text-keepui-text dark:bg-keepui-primary border border-keepui-primary',
141
- 'enabled:hover:bg-keepui-primary-hover enabled:hover:border-keepui-primary-hover',
142
- 'enabled:active:bg-keepui-primary-active enabled:active:border-keepui-primary-active',
140
+ 'bg-ku-primary text-ku-primary-text border border-ku-primary-border',
141
+ 'enabled:hover:bg-ku-primary-hover enabled:hover:border-ku-primary-border',
142
+ 'enabled:active:bg-ku-primary-hover enabled:active:border-ku-primary-border',
143
143
  ].join(' '),
144
144
  secondary: [
145
- 'bg-keepui-surface text-keepui-text border border-keepui-border',
146
- 'enabled:hover:bg-keepui-surface-hover enabled:hover:border-keepui-border-strong',
147
- 'disabled:text-keepui-text-disabled',
145
+ 'bg-ku-secondary text-ku-secondary-text border border-ku-secondary-border',
146
+ 'enabled:hover:bg-ku-secondary-hover enabled:hover:border-ku-gray-border',
147
+ 'disabled:text-ku-gray-text',
148
148
  ].join(' '),
149
149
  outline: [
150
- 'bg-transparent border border-keepui-border text-keepui-text',
151
- 'enabled:hover:border-keepui-primary enabled:hover:text-keepui-primary',
150
+ 'bg-transparent border border-ku-secondary-border text-ku-secondary-text',
151
+ 'enabled:hover:border-ku-primary-border enabled:hover:text-ku-brand-text',
152
152
  ].join(' '),
153
153
  ghost: [
154
- 'bg-transparent border border-keepui-primary text-keepui-primary',
155
- 'enabled:hover:bg-keepui-primary/10',
154
+ 'bg-transparent border border-ku-primary-border text-ku-brand-text',
155
+ 'enabled:hover:bg-ku-primary/10',
156
156
  ].join(' '),
157
157
  danger: [
158
- 'bg-keepui-error text-keepui-error-fg border border-keepui-error',
158
+ 'bg-ku-red-bg text-ku-red-text border border-ku-red-border',
159
159
  'enabled:hover:opacity-90',
160
160
  'enabled:active:opacity-80',
161
161
  ].join(' '),
@@ -224,55 +224,126 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
224
224
  }] });
225
225
 
226
226
  /**
227
- * A versatile card container component.
227
+ * Contenedor versátil con soporte de variante, padding, color, estado clickable,
228
+ * seleccionado y scrollable.
229
+ *
230
+ * `padding="screen"` aplica padding lateral y superior pero omite el inferior
231
+ * intencionadamente para que el contenido parezca continuar más allá del área visible,
232
+ * invitando al usuario a hacer scroll. Un `div` espaciador se inserta automáticamente
233
+ * al final del contenido proyectado: cuando el usuario llega al fondo, el espaciador
234
+ * reproduce el padding inferior correcto sin que el consumidor deba declararlo.
228
235
  *
229
236
  * ```html
230
- * <keepui-card>
231
- * <h2>Title</h2>
232
- * <p>Some content</p>
233
- * </keepui-card>
237
+ * <keepui-card>Contenido</keepui-card>
234
238
  *
235
- * <keepui-card [elevation]="2">
236
- * Elevated card
239
+ * <keepui-card variant="flat" padding="lg" [clickable]="true" (clicked)="onSelect()">
240
+ * Card clicable
237
241
  * </keepui-card>
242
+ *
243
+ * <div class="h-screen overflow-hidden">
244
+ * <keepui-card padding="screen" [scrollable]="true" [fullHeight]="true">
245
+ * Contenido largo — el padding inferior se gestiona automáticamente
246
+ * </keepui-card>
247
+ * </div>
238
248
  * ```
239
249
  */
240
250
  class CardComponent {
241
251
  constructor() {
242
- /** Shadow elevation level (0–3). */
243
- this.elevation = input(1);
252
+ this.BASE_CLASS = 'rounded-xl transition-colors overflow-hidden';
253
+ this.VARIANT_CLASS_MAP = {
254
+ flat: '',
255
+ outlined: 'border border-ku-secondary-border',
256
+ };
257
+ this.SELECTED_VARIANT_CLASS_MAP = {
258
+ flat: 'border border-ku-brand-border',
259
+ outlined: 'border border-ku-brand-border',
260
+ };
261
+ this.PADDING_CLASS_MAP = {
262
+ none: '',
263
+ sm: 'p-3',
264
+ md: 'p-4',
265
+ lg: 'p-6',
266
+ screen: 'px-4 pt-4',
267
+ };
268
+ this.COLORS_CLASS_MAP = {
269
+ primary: 'bg-ku-primary',
270
+ secondary: 'bg-ku-secondary',
271
+ };
272
+ this.variant = input('outlined');
273
+ this.padding = input('md');
274
+ this.colors = input('primary');
275
+ this.clickable = input(false);
276
+ this.selected = input(false);
277
+ this.scrollable = input(false);
278
+ this.fullHeight = input(false);
279
+ this.clicked = output();
244
280
  this.cardClass = computed(() => {
245
- const base = [
246
- 'rounded-lg p-4',
247
- 'bg-keepui-surface border border-keepui-border text-keepui-text',
248
- 'transition-all duration-200',
249
- ].join(' ');
250
- const shadowMap = {
251
- 0: '',
252
- 1: 'shadow-keepui-sm',
253
- 2: 'shadow-keepui-md',
254
- 3: 'shadow-keepui-lg',
255
- };
256
- const shadow = shadowMap[this.elevation()];
257
- return shadow ? `${base} ${shadow}` : base;
281
+ const variantClass = this.selected()
282
+ ? this.SELECTED_VARIANT_CLASS_MAP[this.variant()]
283
+ : this.VARIANT_CLASS_MAP[this.variant()];
284
+ const classes = [
285
+ this.BASE_CLASS,
286
+ this.COLORS_CLASS_MAP[this.colors()],
287
+ variantClass,
288
+ this.PADDING_CLASS_MAP[this.padding()],
289
+ ];
290
+ if (this.clickable()) {
291
+ classes.push('cursor-pointer hover:bg-ku-primary-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-ku-brand-border focus-visible:ring-offset-2');
292
+ }
293
+ if (this.scrollable()) {
294
+ classes.push('overflow-y-auto', 'overflow-x-auto');
295
+ }
296
+ if (this.fullHeight()) {
297
+ classes.push('h-full');
298
+ }
299
+ return classes.filter(Boolean).join(' ');
258
300
  });
259
301
  }
302
+ onCardClick() {
303
+ if (this.clickable()) {
304
+ this.clicked.emit();
305
+ }
306
+ }
260
307
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
261
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.20", type: CardComponent, isStandalone: true, selector: "keepui-card", inputs: { elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
262
- <div [class]="cardClass()">
308
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", type: CardComponent, isStandalone: true, selector: "keepui-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, clickable: { classPropertyName: "clickable", publicName: "clickable", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, fullHeight: { classPropertyName: "fullHeight", publicName: "fullHeight", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, host: { properties: { "class.h-full": "fullHeight()" }, classAttribute: "block" }, ngImport: i0, template: `
309
+ <div
310
+ [class]="cardClass()"
311
+ (click)="onCardClick()"
312
+ [attr.role]="clickable() ? 'button' : null"
313
+ [attr.tabindex]="clickable() ? 0 : null"
314
+ (keydown.enter)="onCardClick()"
315
+ (keydown.space)="onCardClick()"
316
+ >
263
317
  <ng-content />
318
+ @if (padding() === 'screen') {
319
+ <div class="h-4 shrink-0" aria-hidden="true"></div>
320
+ }
264
321
  </div>
265
- `, isInline: true }); }
322
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
266
323
  }
267
324
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CardComponent, decorators: [{
268
325
  type: Component,
269
326
  args: [{
270
327
  selector: 'keepui-card',
271
328
  standalone: true,
272
- host: { class: 'block' },
329
+ changeDetection: ChangeDetectionStrategy.OnPush,
330
+ host: {
331
+ class: 'block',
332
+ '[class.h-full]': 'fullHeight()',
333
+ },
273
334
  template: `
274
- <div [class]="cardClass()">
335
+ <div
336
+ [class]="cardClass()"
337
+ (click)="onCardClick()"
338
+ [attr.role]="clickable() ? 'button' : null"
339
+ [attr.tabindex]="clickable() ? 0 : null"
340
+ (keydown.enter)="onCardClick()"
341
+ (keydown.space)="onCardClick()"
342
+ >
275
343
  <ng-content />
344
+ @if (padding() === 'screen') {
345
+ <div class="h-4 shrink-0" aria-hidden="true"></div>
346
+ }
276
347
  </div>
277
348
  `,
278
349
  }]
@@ -375,7 +446,7 @@ class ImagePreviewComponent {
375
446
  @if (imageUrl()) {
376
447
  <div class="max-w-full">
377
448
  <img
378
- class="max-w-full h-auto rounded border border-keepui-border"
449
+ class="max-w-full h-auto rounded border border-ku-secondary-border"
379
450
  [src]="imageUrl()"
380
451
  [attr.alt]="keys.PREVIEW_ALT | transloco"
381
452
  />
@@ -383,7 +454,7 @@ class ImagePreviewComponent {
383
454
  }
384
455
 
385
456
  @if (error()) {
386
- <p class="text-keepui-error text-sm m-0" role="alert">{{ error() }}</p>
457
+ <p class="text-ku-red-text text-sm m-0" role="alert">{{ error() }}</p>
387
458
  }
388
459
 
389
460
  </div>
@@ -421,7 +492,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
421
492
  @if (imageUrl()) {
422
493
  <div class="max-w-full">
423
494
  <img
424
- class="max-w-full h-auto rounded border border-keepui-border"
495
+ class="max-w-full h-auto rounded border border-ku-secondary-border"
425
496
  [src]="imageUrl()"
426
497
  [attr.alt]="keys.PREVIEW_ALT | transloco"
427
498
  />
@@ -429,7 +500,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
429
500
  }
430
501
 
431
502
  @if (error()) {
432
- <p class="text-keepui-error text-sm m-0" role="alert">{{ error() }}</p>
503
+ <p class="text-ku-red-text text-sm m-0" role="alert">{{ error() }}</p>
433
504
  }
434
505
 
435
506
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"keepui-ui.mjs","sources":["../../../projects/keep-ui/src/lib/tokens/file.token.ts","../../../projects/keep-ui/src/lib/services/web-file.service.ts","../../../projects/keep-ui/src/lib/components/button/button.component.ts","../../../projects/keep-ui/src/lib/components/card/card.component.ts","../../../projects/keep-ui/src/lib/i18n/translation-keys.ts","../../../projects/keep-ui/src/lib/components/image-preview/image-preview.component.ts","../../../projects/keep-ui/src/lib/providers/provide-keep-ui.ts","../../../projects/keep-ui/src/lib/i18n/keep-ui-translations.ts","../../../projects/keep-ui/src/lib/services/keep-ui-transloco-loader.service.ts","../../../projects/keep-ui/src/lib/services/keep-ui-language.service.ts","../../../projects/keep-ui/src/lib/providers/keep-ui-i18n.provider.ts","../../../projects/keep-ui/src/lib/testing/mock-file.service.ts","../../../projects/keep-ui/src/public-api.ts","../../../projects/keep-ui/src/keepui-ui.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\r\nimport { FilePort } from '../ports/file.port';\r\n\r\n/**\r\n * Injection token for the platform file/image adapter.\r\n *\r\n * Register a concrete implementation with:\r\n * - `provideKeepUi()` for web projects\r\n * - `provideKeepUiCapacitor()` for Angular + Capacitor projects\r\n */\r\nexport const FILE_PORT = new InjectionToken<FilePort>('FILE_PORT');\r\n","import { Injectable } from '@angular/core';\r\nimport { FilePort } from '../ports/file.port';\r\nimport { FileResult } from '../models/file-result.model';\r\n\r\n/**\r\n * Web implementation of `FilePort`.\r\n *\r\n * Uses a hidden `<input type=\"file\">` and the `FileReader` API to let the user\r\n * pick an image from the file system in a browser environment.\r\n *\r\n * This implementation has no dependency on Capacitor or any native plugin.\r\n */\r\n@Injectable()\r\nexport class WebFileService implements FilePort {\r\n pickImage(): Promise<FileResult> {\r\n return new Promise<FileResult>((resolve, reject) => {\r\n const input = document.createElement('input');\r\n input.type = 'file';\r\n input.accept = 'image/*';\r\n\r\n input.onchange = () => {\r\n const file = input.files?.[0];\r\n if (!file) {\r\n reject(new Error('No file selected'));\r\n return;\r\n }\r\n\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n resolve({\r\n dataUrl: reader.result as string,\r\n mimeType: file.type,\r\n });\r\n };\r\n reader.onerror = () => reject(reader.error ?? new Error('FileReader error'));\r\n reader.readAsDataURL(file);\r\n };\r\n\r\n input.click();\r\n });\r\n }\r\n}\r\n","import { Component, ChangeDetectionStrategy, computed, input, output } from '@angular/core';\r\nimport { ButtonVariant, ButtonSize, ButtonShape, ButtonType } from './button.types';\r\n\r\n/**\r\n * Accessible, themed action button with support for variants, shapes, sizes,\r\n * loading state, full-width layout, and named icon slots.\r\n *\r\n * Works identically on **web** and **Angular + Capacitor** (no native API usage).\r\n *\r\n * ```html\r\n * <!-- Basic usage -->\r\n * <keepui-button (clicked)=\"save()\">Save</keepui-button>\r\n *\r\n * <!-- Primary, pill-shaped, fixed width -->\r\n * <keepui-button variant=\"primary\" shape=\"pill\">Confirm</keepui-button>\r\n *\r\n * <!-- With leading icon (any inline element) -->\r\n * <keepui-button variant=\"outline\" size=\"auto\">\r\n * <svg slot=\"leading\" width=\"16\" height=\"16\" aria-hidden=\"true\">…</svg>\r\n * Upload\r\n * </keepui-button>\r\n *\r\n * <!-- Loading state -->\r\n * <keepui-button [loading]=\"isSaving()\">Saving…</keepui-button>\r\n *\r\n * <!-- Full-width, danger -->\r\n * <keepui-button variant=\"danger\" [fullWidth]=\"true\">Delete account</keepui-button>\r\n *\r\n * <!-- Icon-only (requires ariaLabel for accessibility) -->\r\n * <keepui-button variant=\"ghost\" size=\"auto\" ariaLabel=\"Close dialog\">\r\n * <svg slot=\"leading\" …>…</svg>\r\n * </keepui-button>\r\n * ```\r\n */\r\n@Component({\r\n selector: 'keepui-button',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {\r\n '[style.display]': 'fullWidth() ? \"block\" : \"inline-block\"',\r\n '[style.width]': 'fullWidth() ? \"100%\" : null',\r\n },\r\n template: `\r\n <button\r\n [attr.type]=\"type()\"\r\n [disabled]=\"isDisabled()\"\r\n [attr.aria-disabled]=\"isDisabled() ? true : null\"\r\n [attr.aria-busy]=\"loading() ? true : null\"\r\n [attr.aria-label]=\"ariaLabel() || null\"\r\n (click)=\"handleClick()\"\r\n [class]=\"buttonClass()\"\r\n >\r\n @if (loading()) {\r\n <span class=\"keepui-btn-spinner\" aria-hidden=\"true\"></span>\r\n } @else {\r\n <ng-content select=\"[slot='leading']\" />\r\n }\r\n\r\n <ng-content />\r\n\r\n @if (!loading()) {\r\n <ng-content select=\"[slot='trailing']\" />\r\n }\r\n </button>\r\n `,\r\n styles: [`\r\n .keepui-btn-spinner {\r\n display: inline-block;\r\n width: 1em;\r\n height: 1em;\r\n border: 2px solid currentColor;\r\n border-top-color: transparent;\r\n border-radius: 50%;\r\n animation: keepui-spin 0.65s linear infinite;\r\n flex-shrink: 0;\r\n }\r\n\r\n @keyframes keepui-spin {\r\n to { transform: rotate(360deg); }\r\n }\r\n `],\r\n})\r\nexport class ButtonComponent {\r\n /** Visual style of the button. @default 'primary' */\r\n readonly variant = input<ButtonVariant>('primary');\r\n\r\n /**\r\n * Size mode.\r\n * - `md` → fixed 160 px wide, 40 px tall.\r\n * - `auto` → padding-driven width, 40 px tall.\r\n * @default 'md'\r\n */\r\n readonly size = input<ButtonSize>('md');\r\n\r\n /** Border-radius style. @default 'pill' */\r\n readonly shape = input<ButtonShape>('pill');\r\n\r\n /** HTML `type` attribute of the inner `<button>`. @default 'button' */\r\n readonly type = input<ButtonType>('button');\r\n\r\n /** Disables the button when `true`. @default false */\r\n readonly disabled = input(false);\r\n\r\n /**\r\n * Replaces the content with an animated spinner and sets `aria-busy`.\r\n * Also disables the button until the operation completes.\r\n * @default false\r\n */\r\n readonly loading = input(false);\r\n\r\n /** Expands the button to fill its container width. @default false */\r\n readonly fullWidth = input(false);\r\n\r\n /**\r\n * Accessible label for icon-only buttons.\r\n * When provided, sets the `aria-label` attribute on the inner `<button>`.\r\n * @default ''\r\n */\r\n readonly ariaLabel = input('');\r\n\r\n /** Emitted when the button is clicked and is not disabled or loading. */\r\n readonly clicked = output<void>();\r\n\r\n protected readonly isDisabled = computed(() => this.disabled() || this.loading());\r\n\r\n protected readonly buttonClass = computed(() => {\r\n const base = [\r\n 'inline-flex items-center justify-center gap-2',\r\n 'min-h-[2.75rem] text-sm font-medium cursor-pointer select-none',\r\n 'transition-colors duration-200',\r\n 'focus-visible:outline-none focus-visible:ring-2',\r\n 'focus-visible:ring-keepui-primary focus-visible:ring-offset-2',\r\n 'disabled:opacity-50 disabled:cursor-not-allowed',\r\n ].join(' ');\r\n\r\n const sizeClass = this.fullWidth()\r\n ? 'w-full h-10 px-4'\r\n : this.size() === 'md'\r\n ? 'w-40 h-10'\r\n : 'px-6 h-10';\r\n\r\n const shapeMap: Record<ButtonShape, string> = {\r\n pill: 'rounded-full',\r\n rounded: 'rounded-2xl',\r\n };\r\n\r\n const variantMap: Record<ButtonVariant, string> = {\r\n primary: [\r\n 'bg-keepui-primary text-keepui-text dark:bg-keepui-primary border border-keepui-primary',\r\n 'enabled:hover:bg-keepui-primary-hover enabled:hover:border-keepui-primary-hover',\r\n 'enabled:active:bg-keepui-primary-active enabled:active:border-keepui-primary-active',\r\n ].join(' '),\r\n secondary: [\r\n 'bg-keepui-surface text-keepui-text border border-keepui-border',\r\n 'enabled:hover:bg-keepui-surface-hover enabled:hover:border-keepui-border-strong',\r\n 'disabled:text-keepui-text-disabled',\r\n ].join(' '),\r\n outline: [\r\n 'bg-transparent border border-keepui-border text-keepui-text',\r\n 'enabled:hover:border-keepui-primary enabled:hover:text-keepui-primary',\r\n ].join(' '),\r\n ghost: [\r\n 'bg-transparent border border-keepui-primary text-keepui-primary',\r\n 'enabled:hover:bg-keepui-primary/10',\r\n ].join(' '),\r\n danger: [\r\n 'bg-keepui-error text-keepui-error-fg border border-keepui-error',\r\n 'enabled:hover:opacity-90',\r\n 'enabled:active:opacity-80',\r\n ].join(' '),\r\n };\r\n\r\n return [base, sizeClass, shapeMap[this.shape()], variantMap[this.variant()]].join(' ');\r\n });\r\n\r\n protected handleClick(): void {\r\n if (!this.isDisabled()) {\r\n this.clicked.emit();\r\n }\r\n }\r\n}\r\n","import { Component, computed, input } from '@angular/core';\r\n\r\n/**\r\n * A versatile card container component.\r\n *\r\n * ```html\r\n * <keepui-card>\r\n * <h2>Title</h2>\r\n * <p>Some content</p>\r\n * </keepui-card>\r\n *\r\n * <keepui-card [elevation]=\"2\">\r\n * Elevated card\r\n * </keepui-card>\r\n * ```\r\n */\r\n@Component({\r\n selector: 'keepui-card',\r\n standalone: true,\r\n host: { class: 'block' },\r\n template: `\r\n <div [class]=\"cardClass()\">\r\n <ng-content />\r\n </div>\r\n `,\r\n})\r\nexport class CardComponent {\r\n /** Shadow elevation level (0–3). */\r\n readonly elevation = input<0 | 1 | 2 | 3>(1);\r\n\r\n protected readonly cardClass = computed(() => {\r\n const base = [\r\n 'rounded-lg p-4',\r\n 'bg-keepui-surface border border-keepui-border text-keepui-text',\r\n 'transition-all duration-200',\r\n ].join(' ');\r\n\r\n const shadowMap: Record<0 | 1 | 2 | 3, string> = {\r\n 0: '',\r\n 1: 'shadow-keepui-sm',\r\n 2: 'shadow-keepui-md',\r\n 3: 'shadow-keepui-lg',\r\n };\r\n\r\n const shadow = shadowMap[this.elevation()];\r\n return shadow ? `${base} ${shadow}` : base;\r\n });\r\n}\r\n","/**\r\n * Typed constants for every translation key used in @keepui/ui.\r\n *\r\n * Use these instead of raw strings so that renaming a key is caught by\r\n * the TypeScript compiler across all consumers.\r\n *\r\n * @example\r\n * ```ts\r\n * import { KEEPUI_TRANSLATION_KEYS as T } from '@keepui/ui';\r\n *\r\n * const label = translocoService.translate(T.IMAGE_PREVIEW.SELECT_IMAGE);\r\n * ```\r\n */\r\nexport const KEEPUI_TRANSLATION_KEYS = {\r\n IMAGE_PREVIEW: {\r\n SELECT_IMAGE: 'imagePreview.selectImage',\r\n LOADING: 'imagePreview.loading',\r\n PREVIEW_ALT: 'imagePreview.previewAlt',\r\n ERROR_UNEXPECTED: 'imagePreview.errorUnexpected',\r\n },\r\n} as const;\r\n\r\n/** Union of all supported KeepUI locales. */\r\nexport type KeepUiLanguage = 'en' | 'es' | 'de';\r\n\r\n/** Ordered list of languages available in KeepUI. */\r\nexport const KEEPUI_AVAILABLE_LANGUAGES: readonly KeepUiLanguage[] = [\r\n 'en',\r\n 'es',\r\n 'de',\r\n] as const;\r\n\r\n","import { Component, ChangeDetectionStrategy, inject, signal } from '@angular/core';\r\nimport { TranslocoPipe, TRANSLOCO_SCOPE } from '@jsverse/transloco';\r\nimport { FILE_PORT } from '../../tokens/file.token';\r\nimport { KEEPUI_TRANSLATION_KEYS as T } from '../../i18n/translation-keys';\r\nimport { ButtonComponent } from '../button/button.component';\r\n\r\n/**\r\n * Standalone component that allows the user to pick and preview an image.\r\n *\r\n * This component is fully platform-agnostic. It delegates file picking to\r\n * whatever `FilePort` implementation is provided via `FILE_PORT`.\r\n *\r\n * UI strings are fully internationalised via Transloco (scope `'keepui'`).\r\n * Call `provideKeepUiI18n()` in your `app.config.ts` and use\r\n * `KeepUiLanguageService.setLanguage(lang)` to change locale at runtime.\r\n *\r\n * Usage:\r\n * ```html\r\n * <keepui-image-preview />\r\n * ```\r\n *\r\n * Prerequisites — register providers in `app.config.ts`:\r\n * - Web: `provideKeepUi()` + `provideKeepUiI18n()`\r\n * - Capacitor: `provideKeepUiCapacitor()` + `provideKeepUiI18n()`\r\n */\r\n@Component({\r\n selector: 'keepui-image-preview',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [TranslocoPipe, ButtonComponent],\r\n providers: [{ provide: TRANSLOCO_SCOPE, useValue: 'keepui' }],\r\n host: { class: 'block' },\r\n template: `\r\n <div class=\"flex flex-col gap-4\">\r\n\r\n <keepui-button\r\n type=\"button\"\r\n variant=\"secondary\"\r\n size=\"auto\"\r\n shape=\"rounded\"\r\n [loading]=\"loading()\"\r\n (clicked)=\"pickImage()\"\r\n >\r\n {{ (loading() ? keys.LOADING : keys.SELECT_IMAGE) | transloco }}\r\n </keepui-button>\r\n\r\n <span role=\"status\" class=\"sr-only\">\r\n @if (imageUrl()) {\r\n {{ keys.PREVIEW_ALT | transloco }}\r\n }\r\n </span>\r\n\r\n @if (imageUrl()) {\r\n <div class=\"max-w-full\">\r\n <img\r\n class=\"max-w-full h-auto rounded border border-keepui-border\"\r\n [src]=\"imageUrl()\"\r\n [attr.alt]=\"keys.PREVIEW_ALT | transloco\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (error()) {\r\n <p class=\"text-keepui-error text-sm m-0\" role=\"alert\">{{ error() }}</p>\r\n }\r\n\r\n </div>\r\n `,\r\n})\r\nexport class ImagePreviewComponent {\r\n private readonly filePort = inject(FILE_PORT);\r\n\r\n /** Translation key references (typed via KEEPUI_TRANSLATION_KEYS). */\r\n protected readonly keys = T.IMAGE_PREVIEW;\r\n\r\n /** URL of the selected image, ready to bind to `[src]`. */\r\n readonly imageUrl = signal<string | null>(null);\r\n /** Error message if the last pick operation failed. */\r\n readonly error = signal<string | null>(null);\r\n /** True while the pick operation is in progress. */\r\n readonly loading = signal(false);\r\n\r\n async pickImage(): Promise<void> {\r\n this.error.set(null);\r\n this.loading.set(true);\r\n\r\n try {\r\n const result = await this.filePort.pickImage();\r\n this.imageUrl.set(result.dataUrl);\r\n } catch (err) {\r\n this.error.set(\r\n err instanceof Error ? err.message : T.IMAGE_PREVIEW.ERROR_UNEXPECTED,\r\n );\r\n } finally {\r\n this.loading.set(false);\r\n }\r\n }\r\n}\r\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\r\nimport { FILE_PORT } from '../tokens/file.token';\r\nimport { WebFileService } from '../services/web-file.service';\r\n\r\n/**\r\n * Registers KeepUI core providers for a **web** Angular application.\r\n *\r\n * Registers `WebFileService` as the implementation of `FILE_PORT`, enabling\r\n * all KeepUI components to use the browser's native file picker.\r\n *\r\n * Add to `app.config.ts`:\r\n * ```ts\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [provideKeepUi()],\r\n * };\r\n * ```\r\n */\r\nexport function provideKeepUi(): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n { provide: FILE_PORT, useClass: WebFileService },\r\n ]);\r\n}\r\n","/**\r\n * Inline translation objects for KeepUI.\r\n *\r\n * These mirror `en.json`, `es.json` and `de.json` and are bundled directly\r\n * into the library so that no HTTP request is needed at runtime.\r\n *\r\n * > **Do not edit this file manually.**\r\n * > Keep it in sync with the JSON files inside `/i18n/`.\r\n */\r\n\r\nimport { KeepUiLanguage } from './translation-keys';\r\n\r\nexport interface KeepUiTranslationSchema {\r\n imagePreview: {\r\n selectImage: string;\r\n loading: string;\r\n previewAlt: string;\r\n errorUnexpected: string;\r\n };\r\n}\r\n\r\nconst EN: KeepUiTranslationSchema = {\r\n imagePreview: {\r\n selectImage: 'Select image',\r\n loading: 'Loading…',\r\n previewAlt: 'Selected image preview',\r\n errorUnexpected: 'An unexpected error occurred',\r\n },\r\n};\r\n\r\nconst ES: KeepUiTranslationSchema = {\r\n imagePreview: {\r\n selectImage: 'Seleccionar imagen',\r\n loading: 'Cargando…',\r\n previewAlt: 'Vista previa de imagen seleccionada',\r\n errorUnexpected: 'Ha ocurrido un error inesperado',\r\n },\r\n};\r\n\r\nconst DE: KeepUiTranslationSchema = {\r\n imagePreview: {\r\n selectImage: 'Bild auswählen',\r\n loading: 'Laden…',\r\n previewAlt: 'Vorschau des ausgewählten Bildes',\r\n errorUnexpected: 'Ein unerwarteter Fehler ist aufgetreten',\r\n },\r\n};\r\n\r\n/** Map from locale code to its translation object. */\r\nexport const KEEPUI_TRANSLATIONS: Record<KeepUiLanguage, KeepUiTranslationSchema> = {\r\n en: EN,\r\n es: ES,\r\n de: DE,\r\n};\r\n\r\n","import { Injectable } from '@angular/core';\r\nimport { Translation, TranslocoLoader } from '@jsverse/transloco';\r\nimport { Observable, of } from 'rxjs';\r\nimport { KeepUiLanguage } from '../i18n/translation-keys';\r\nimport { KEEPUI_TRANSLATIONS } from '../i18n/keep-ui-translations';\r\n\r\n/**\r\n * Transloco loader for KeepUI.\r\n *\r\n * Serves translations **inline** (bundled in the library JS) so that no HTTP\r\n * request is required. It handles both plain lang paths (`'en'`, `'es'`, …)\r\n * and scope-prefixed paths (`'keepui/en'`, `'keepui/es'`, …).\r\n *\r\n * @internal\r\n */\r\n@Injectable()\r\nexport class KeepUiTranslocoLoader implements TranslocoLoader {\r\n getTranslation(langPath: string): Observable<Translation> {\r\n // Transloco calls this with 'keepui/en', 'keepui/es', etc.\r\n // when a component has TRANSLOCO_SCOPE = 'keepui'.\r\n const lang = langPath.includes('/') ? langPath.split('/')[1] : langPath;\r\n\r\n const isValid = (l: string): l is KeepUiLanguage =>\r\n l === 'en' || l === 'es' || l === 'de';\r\n\r\n const translations = isValid(lang)\r\n ? KEEPUI_TRANSLATIONS[lang]\r\n : KEEPUI_TRANSLATIONS['en'];\r\n\r\n return of(translations as unknown as Translation);\r\n }\r\n}\r\n\r\n","import { Injectable, inject, signal } from '@angular/core';\r\nimport { TranslocoService } from '@jsverse/transloco';\r\nimport { KeepUiLanguage, KEEPUI_AVAILABLE_LANGUAGES } from '../i18n/translation-keys';\r\n\r\n/**\r\n * Service that exposes a public API for changing the active language of all\r\n * KeepUI components at runtime.\r\n *\r\n * ### Usage\r\n * ```ts\r\n * // Inject anywhere in the host application\r\n * const lang = inject(KeepUiLanguageService);\r\n *\r\n * // Switch to Spanish\r\n * lang.setLanguage('es');\r\n *\r\n * // Read the active language\r\n * console.log(lang.activeLanguage()); // 'es'\r\n * ```\r\n *\r\n * Register via `provideKeepUiI18n()` in `app.config.ts`.\r\n */\r\n@Injectable()\r\nexport class KeepUiLanguageService {\r\n private readonly transloco = inject(TranslocoService);\r\n\r\n /** Signal that reflects the currently active KeepUI locale. */\r\n readonly activeLanguage = signal<KeepUiLanguage>(\r\n this.transloco.getActiveLang() as KeepUiLanguage,\r\n );\r\n\r\n /** Ordered list of all supported locales. */\r\n readonly availableLanguages: readonly KeepUiLanguage[] = KEEPUI_AVAILABLE_LANGUAGES;\r\n\r\n /**\r\n * Changes the active language for all KeepUI components.\r\n *\r\n * @param lang - One of `'en'`, `'es'` or `'de'`.\r\n */\r\n setLanguage(lang: KeepUiLanguage): void {\r\n this.transloco.setActiveLang(lang);\r\n this.activeLanguage.set(lang);\r\n }\r\n}\r\n\r\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\r\nimport { provideTransloco } from '@jsverse/transloco';\r\nimport { KeepUiLanguage, KEEPUI_AVAILABLE_LANGUAGES } from '../i18n/translation-keys';\r\nimport { KeepUiTranslocoLoader } from '../services/keep-ui-transloco-loader.service';\r\nimport { KeepUiLanguageService } from '../services/keep-ui-language.service';\r\n\r\n/**\r\n * Options for `provideKeepUiI18n()`.\r\n */\r\nexport interface KeepUiI18nOptions {\r\n /**\r\n * The default language to use when the app starts.\r\n * @default 'en'\r\n */\r\n defaultLang?: KeepUiLanguage;\r\n}\r\n\r\n/**\r\n * Registers all providers required for KeepUI internationalisation.\r\n *\r\n * - Configures a self-contained Transloco instance scoped to `'keepui'`.\r\n * - Bundles all translations **inline** — no HTTP assets required.\r\n * - Provides {@link KeepUiLanguageService} so the host app can switch locale.\r\n *\r\n * ### Usage in `app.config.ts`\r\n * ```ts\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [\r\n * provideKeepUi(),\r\n * provideKeepUiI18n(), // default language: 'en'\r\n * provideKeepUiI18n({ defaultLang: 'es' }), // or start in Spanish\r\n * ],\r\n * };\r\n * ```\r\n *\r\n * ### Changing language at runtime\r\n * ```ts\r\n * const lang = inject(KeepUiLanguageService);\r\n * lang.setLanguage('de');\r\n * ```\r\n *\r\n * > **Note:** If your application already calls `provideTransloco()`, do NOT\r\n * > call `provideKeepUiI18n()` — instead configure your Transloco loader to\r\n * > handle the `'keepui/{lang}'` scope paths, and provide `KeepUiLanguageService`\r\n * > manually.\r\n */\r\nexport function provideKeepUiI18n(\r\n options: KeepUiI18nOptions = {},\r\n): EnvironmentProviders {\r\n const defaultLang: KeepUiLanguage = options.defaultLang ?? 'en';\r\n\r\n return makeEnvironmentProviders([\r\n provideTransloco({\r\n config: {\r\n defaultLang,\r\n availableLangs: [...KEEPUI_AVAILABLE_LANGUAGES],\r\n reRenderOnLangChange: true,\r\n failedRetries: 0,\r\n missingHandler: { useFallbackTranslation: true },\r\n fallbackLang: 'en',\r\n },\r\n loader: KeepUiTranslocoLoader,\r\n }),\r\n\r\n KeepUiLanguageService,\r\n ]);\r\n}\r\n\r\n","import { Injectable } from '@angular/core';\r\nimport { FilePort } from '../ports/file.port';\r\nimport { FileResult } from '../models/file-result.model';\r\n\r\n/**\r\n * A controllable mock implementation of `FilePort` for use in unit tests.\r\n *\r\n * By default, `pickImage()` resolves successfully. Set `nextError` to make the\r\n * next call reject with that error.\r\n *\r\n * ```ts\r\n * providers: [{ provide: FILE_PORT, useClass: MockFileService }]\r\n * ```\r\n */\r\n@Injectable()\r\nexport class MockFileService implements FilePort {\r\n /** Set this to make the next `pickImage()` call reject with this error. */\r\n nextError: Error | null = null;\r\n\r\n pickImage(): Promise<FileResult> {\r\n if (this.nextError) {\r\n const err = this.nextError;\r\n this.nextError = null;\r\n return Promise.reject(err);\r\n }\r\n return Promise.resolve({\r\n dataUrl: 'data:image/jpeg;base64,mockImageData==',\r\n mimeType: 'image/jpeg',\r\n });\r\n }\r\n}\r\n","// Public API Surface of @keepui/ui\r\n\r\n// Models\r\nexport * from './lib/models/file-result.model';\r\n\r\n// Ports (interfaces)\r\nexport * from './lib/ports/file.port';\r\n\r\n// Injection tokens\r\nexport * from './lib/tokens/file.token';\r\n\r\n// Services\r\nexport * from './lib/services/web-file.service';\r\n\r\n// Components\r\nexport * from './lib/components/button/button.types';\r\nexport * from './lib/components/button/button.component';\r\nexport * from './lib/components/card/card.component';\r\nexport * from './lib/components/image-preview/image-preview.component';\r\n\r\n// Providers\r\nexport * from './lib/providers/provide-keep-ui';\r\nexport * from './lib/providers/keep-ui-i18n.provider';\r\n\r\n// i18n – translation keys & language utilities\r\nexport * from './lib/i18n/translation-keys';\r\nexport * from './lib/i18n/keep-ui-translations';\r\nexport * from './lib/services/keep-ui-language.service';\r\n\r\n// Testing utilities\r\nexport * from './lib/testing/mock-file.service';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["T"],"mappings":"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"}
1
+ {"version":3,"file":"keepui-ui.mjs","sources":["../../../projects/keep-ui/src/lib/tokens/file.token.ts","../../../projects/keep-ui/src/lib/services/web-file.service.ts","../../../projects/keep-ui/src/lib/components/button/button.component.ts","../../../projects/keep-ui/src/lib/components/card/card.component.ts","../../../projects/keep-ui/src/lib/i18n/translation-keys.ts","../../../projects/keep-ui/src/lib/components/image-preview/image-preview.component.ts","../../../projects/keep-ui/src/lib/providers/provide-keep-ui.ts","../../../projects/keep-ui/src/lib/i18n/keep-ui-translations.ts","../../../projects/keep-ui/src/lib/services/keep-ui-transloco-loader.service.ts","../../../projects/keep-ui/src/lib/services/keep-ui-language.service.ts","../../../projects/keep-ui/src/lib/providers/keep-ui-i18n.provider.ts","../../../projects/keep-ui/src/lib/testing/mock-file.service.ts","../../../projects/keep-ui/src/public-api.ts","../../../projects/keep-ui/src/keepui-ui.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\r\nimport { FilePort } from '../ports/file.port';\r\n\r\n/**\r\n * Injection token for the platform file/image adapter.\r\n *\r\n * Register a concrete implementation with:\r\n * - `provideKeepUi()` for web projects\r\n * - `provideKeepUiCapacitor()` for Angular + Capacitor projects\r\n */\r\nexport const FILE_PORT = new InjectionToken<FilePort>('FILE_PORT');\r\n","import { Injectable } from '@angular/core';\r\nimport { FilePort } from '../ports/file.port';\r\nimport { FileResult } from '../models/file-result.model';\r\n\r\n/**\r\n * Web implementation of `FilePort`.\r\n *\r\n * Uses a hidden `<input type=\"file\">` and the `FileReader` API to let the user\r\n * pick an image from the file system in a browser environment.\r\n *\r\n * This implementation has no dependency on Capacitor or any native plugin.\r\n */\r\n@Injectable()\r\nexport class WebFileService implements FilePort {\r\n pickImage(): Promise<FileResult> {\r\n return new Promise<FileResult>((resolve, reject) => {\r\n const input = document.createElement('input');\r\n input.type = 'file';\r\n input.accept = 'image/*';\r\n\r\n input.onchange = () => {\r\n const file = input.files?.[0];\r\n if (!file) {\r\n reject(new Error('No file selected'));\r\n return;\r\n }\r\n\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n resolve({\r\n dataUrl: reader.result as string,\r\n mimeType: file.type,\r\n });\r\n };\r\n reader.onerror = () => reject(reader.error ?? new Error('FileReader error'));\r\n reader.readAsDataURL(file);\r\n };\r\n\r\n input.click();\r\n });\r\n }\r\n}\r\n","import { Component, ChangeDetectionStrategy, computed, input, output } from '@angular/core';\r\nimport { ButtonVariant, ButtonSize, ButtonShape, ButtonType } from './button.types';\r\n\r\n/**\r\n * Accessible, themed action button with support for variants, shapes, sizes,\r\n * loading state, full-width layout, and named icon slots.\r\n *\r\n * Works identically on **web** and **Angular + Capacitor** (no native API usage).\r\n *\r\n * ```html\r\n * <!-- Basic usage -->\r\n * <keepui-button (clicked)=\"save()\">Save</keepui-button>\r\n *\r\n * <!-- Primary, pill-shaped, fixed width -->\r\n * <keepui-button variant=\"primary\" shape=\"pill\">Confirm</keepui-button>\r\n *\r\n * <!-- With leading icon (any inline element) -->\r\n * <keepui-button variant=\"outline\" size=\"auto\">\r\n * <svg slot=\"leading\" width=\"16\" height=\"16\" aria-hidden=\"true\">…</svg>\r\n * Upload\r\n * </keepui-button>\r\n *\r\n * <!-- Loading state -->\r\n * <keepui-button [loading]=\"isSaving()\">Saving…</keepui-button>\r\n *\r\n * <!-- Full-width, danger -->\r\n * <keepui-button variant=\"danger\" [fullWidth]=\"true\">Delete account</keepui-button>\r\n *\r\n * <!-- Icon-only (requires ariaLabel for accessibility) -->\r\n * <keepui-button variant=\"ghost\" size=\"auto\" ariaLabel=\"Close dialog\">\r\n * <svg slot=\"leading\" …>…</svg>\r\n * </keepui-button>\r\n * ```\r\n */\r\n@Component({\r\n selector: 'keepui-button',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {\r\n '[style.display]': 'fullWidth() ? \"block\" : \"inline-block\"',\r\n '[style.width]': 'fullWidth() ? \"100%\" : null',\r\n },\r\n template: `\r\n <button\r\n [attr.type]=\"type()\"\r\n [disabled]=\"isDisabled()\"\r\n [attr.aria-disabled]=\"isDisabled() ? true : null\"\r\n [attr.aria-busy]=\"loading() ? true : null\"\r\n [attr.aria-label]=\"ariaLabel() || null\"\r\n (click)=\"handleClick()\"\r\n [class]=\"buttonClass()\"\r\n >\r\n @if (loading()) {\r\n <span class=\"keepui-btn-spinner\" aria-hidden=\"true\"></span>\r\n } @else {\r\n <ng-content select=\"[slot='leading']\" />\r\n }\r\n\r\n <ng-content />\r\n\r\n @if (!loading()) {\r\n <ng-content select=\"[slot='trailing']\" />\r\n }\r\n </button>\r\n `,\r\n styles: [`\r\n .keepui-btn-spinner {\r\n display: inline-block;\r\n width: 1em;\r\n height: 1em;\r\n border: 2px solid currentColor;\r\n border-top-color: transparent;\r\n border-radius: 50%;\r\n animation: keepui-spin 0.65s linear infinite;\r\n flex-shrink: 0;\r\n }\r\n\r\n @keyframes keepui-spin {\r\n to { transform: rotate(360deg); }\r\n }\r\n `],\r\n})\r\nexport class ButtonComponent {\r\n /** Visual style of the button. @default 'primary' */\r\n readonly variant = input<ButtonVariant>('primary');\r\n\r\n /**\r\n * Size mode.\r\n * - `md` → fixed 160 px wide, 40 px tall.\r\n * - `auto` → padding-driven width, 40 px tall.\r\n * @default 'md'\r\n */\r\n readonly size = input<ButtonSize>('md');\r\n\r\n /** Border-radius style. @default 'pill' */\r\n readonly shape = input<ButtonShape>('pill');\r\n\r\n /** HTML `type` attribute of the inner `<button>`. @default 'button' */\r\n readonly type = input<ButtonType>('button');\r\n\r\n /** Disables the button when `true`. @default false */\r\n readonly disabled = input(false);\r\n\r\n /**\r\n * Replaces the content with an animated spinner and sets `aria-busy`.\r\n * Also disables the button until the operation completes.\r\n * @default false\r\n */\r\n readonly loading = input(false);\r\n\r\n /** Expands the button to fill its container width. @default false */\r\n readonly fullWidth = input(false);\r\n\r\n /**\r\n * Accessible label for icon-only buttons.\r\n * When provided, sets the `aria-label` attribute on the inner `<button>`.\r\n * @default ''\r\n */\r\n readonly ariaLabel = input('');\r\n\r\n /** Emitted when the button is clicked and is not disabled or loading. */\r\n readonly clicked = output<void>();\r\n\r\n protected readonly isDisabled = computed(() => this.disabled() || this.loading());\r\n\r\n protected readonly buttonClass = computed(() => {\r\n const base = [\r\n 'inline-flex items-center justify-center gap-2',\r\n 'min-h-[2.75rem] text-sm font-medium cursor-pointer select-none',\r\n 'transition-colors duration-200',\r\n 'focus-visible:outline-none focus-visible:ring-2',\r\n 'focus-visible:ring-ku-primary-border focus-visible:ring-offset-2',\r\n 'disabled:opacity-50 disabled:cursor-not-allowed',\r\n ].join(' ');\r\n\r\n const sizeClass = this.fullWidth()\r\n ? 'w-full h-10 px-4'\r\n : this.size() === 'md'\r\n ? 'w-40 h-10'\r\n : 'px-6 h-10';\r\n\r\n const shapeMap: Record<ButtonShape, string> = {\r\n pill: 'rounded-full',\r\n rounded: 'rounded-2xl',\r\n };\r\n\r\n const variantMap: Record<ButtonVariant, string> = {\r\n primary: [\r\n 'bg-ku-primary text-ku-primary-text border border-ku-primary-border',\r\n 'enabled:hover:bg-ku-primary-hover enabled:hover:border-ku-primary-border',\r\n 'enabled:active:bg-ku-primary-hover enabled:active:border-ku-primary-border',\r\n ].join(' '),\r\n secondary: [\r\n 'bg-ku-secondary text-ku-secondary-text border border-ku-secondary-border',\r\n 'enabled:hover:bg-ku-secondary-hover enabled:hover:border-ku-gray-border',\r\n 'disabled:text-ku-gray-text',\r\n ].join(' '),\r\n outline: [\r\n 'bg-transparent border border-ku-secondary-border text-ku-secondary-text',\r\n 'enabled:hover:border-ku-primary-border enabled:hover:text-ku-brand-text',\r\n ].join(' '),\r\n ghost: [\r\n 'bg-transparent border border-ku-primary-border text-ku-brand-text',\r\n 'enabled:hover:bg-ku-primary/10',\r\n ].join(' '),\r\n danger: [\r\n 'bg-ku-red-bg text-ku-red-text border border-ku-red-border',\r\n 'enabled:hover:opacity-90',\r\n 'enabled:active:opacity-80',\r\n ].join(' '),\r\n };\r\n\r\n return [base, sizeClass, shapeMap[this.shape()], variantMap[this.variant()]].join(' ');\r\n });\r\n\r\n protected handleClick(): void {\r\n if (!this.isDisabled()) {\r\n this.clicked.emit();\r\n }\r\n }\r\n}\r\n","import { Component, ChangeDetectionStrategy, computed, input, output } from '@angular/core';\r\n\r\nexport type CardVariant = 'flat' | 'outlined';\r\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg' | 'screen';\r\nexport type CardColors = 'primary' | 'secondary';\r\n\r\n/**\r\n * Contenedor versátil con soporte de variante, padding, color, estado clickable,\r\n * seleccionado y scrollable.\r\n *\r\n * `padding=\"screen\"` aplica padding lateral y superior pero omite el inferior\r\n * intencionadamente para que el contenido parezca continuar más allá del área visible,\r\n * invitando al usuario a hacer scroll. Un `div` espaciador se inserta automáticamente\r\n * al final del contenido proyectado: cuando el usuario llega al fondo, el espaciador\r\n * reproduce el padding inferior correcto sin que el consumidor deba declararlo.\r\n *\r\n * ```html\r\n * <keepui-card>Contenido</keepui-card>\r\n *\r\n * <keepui-card variant=\"flat\" padding=\"lg\" [clickable]=\"true\" (clicked)=\"onSelect()\">\r\n * Card clicable\r\n * </keepui-card>\r\n *\r\n * <div class=\"h-screen overflow-hidden\">\r\n * <keepui-card padding=\"screen\" [scrollable]=\"true\" [fullHeight]=\"true\">\r\n * Contenido largo — el padding inferior se gestiona automáticamente\r\n * </keepui-card>\r\n * </div>\r\n * ```\r\n */\r\n@Component({\r\n selector: 'keepui-card',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {\r\n class: 'block',\r\n '[class.h-full]': 'fullHeight()',\r\n },\r\n template: `\r\n <div\r\n [class]=\"cardClass()\"\r\n (click)=\"onCardClick()\"\r\n [attr.role]=\"clickable() ? 'button' : null\"\r\n [attr.tabindex]=\"clickable() ? 0 : null\"\r\n (keydown.enter)=\"onCardClick()\"\r\n (keydown.space)=\"onCardClick()\"\r\n >\r\n <ng-content />\r\n @if (padding() === 'screen') {\r\n <div class=\"h-4 shrink-0\" aria-hidden=\"true\"></div>\r\n }\r\n </div>\r\n `,\r\n})\r\nexport class CardComponent {\r\n private readonly BASE_CLASS = 'rounded-xl transition-colors overflow-hidden';\r\n\r\n private readonly VARIANT_CLASS_MAP: Record<CardVariant, string> = {\r\n flat: '',\r\n outlined: 'border border-ku-secondary-border',\r\n };\r\n\r\n private readonly SELECTED_VARIANT_CLASS_MAP: Record<CardVariant, string> = {\r\n flat: 'border border-ku-brand-border',\r\n outlined: 'border border-ku-brand-border',\r\n };\r\n\r\n private readonly PADDING_CLASS_MAP: Record<CardPadding, string> = {\r\n none: '',\r\n sm: 'p-3',\r\n md: 'p-4',\r\n lg: 'p-6',\r\n screen: 'px-4 pt-4',\r\n };\r\n\r\n private readonly COLORS_CLASS_MAP: Record<CardColors, string> = {\r\n primary: 'bg-ku-primary',\r\n secondary: 'bg-ku-secondary',\r\n };\r\n\r\n readonly variant = input<CardVariant>('outlined');\r\n readonly padding = input<CardPadding>('md');\r\n readonly colors = input<CardColors>('primary');\r\n readonly clickable = input<boolean>(false);\r\n readonly selected = input<boolean>(false);\r\n readonly scrollable = input<boolean>(false);\r\n readonly fullHeight = input<boolean>(false);\r\n readonly clicked = output<void>();\r\n\r\n protected readonly cardClass = computed<string>(() => {\r\n const variantClass = this.selected()\r\n ? this.SELECTED_VARIANT_CLASS_MAP[this.variant()]\r\n : this.VARIANT_CLASS_MAP[this.variant()];\r\n\r\n const classes = [\r\n this.BASE_CLASS,\r\n this.COLORS_CLASS_MAP[this.colors()],\r\n variantClass,\r\n this.PADDING_CLASS_MAP[this.padding()],\r\n ];\r\n\r\n if (this.clickable()) {\r\n classes.push(\r\n 'cursor-pointer hover:bg-ku-primary-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-ku-brand-border focus-visible:ring-offset-2'\r\n );\r\n }\r\n\r\n if (this.scrollable()) {\r\n classes.push('overflow-y-auto', 'overflow-x-auto');\r\n }\r\n\r\n if (this.fullHeight()) {\r\n classes.push('h-full');\r\n }\r\n\r\n return classes.filter(Boolean).join(' ');\r\n });\r\n\r\n protected onCardClick(): void {\r\n if (this.clickable()) {\r\n this.clicked.emit();\r\n }\r\n }\r\n}\r\n","/**\r\n * Typed constants for every translation key used in @keepui/ui.\r\n *\r\n * Use these instead of raw strings so that renaming a key is caught by\r\n * the TypeScript compiler across all consumers.\r\n *\r\n * @example\r\n * ```ts\r\n * import { KEEPUI_TRANSLATION_KEYS as T } from '@keepui/ui';\r\n *\r\n * const label = translocoService.translate(T.IMAGE_PREVIEW.SELECT_IMAGE);\r\n * ```\r\n */\r\nexport const KEEPUI_TRANSLATION_KEYS = {\r\n IMAGE_PREVIEW: {\r\n SELECT_IMAGE: 'imagePreview.selectImage',\r\n LOADING: 'imagePreview.loading',\r\n PREVIEW_ALT: 'imagePreview.previewAlt',\r\n ERROR_UNEXPECTED: 'imagePreview.errorUnexpected',\r\n },\r\n} as const;\r\n\r\n/** Union of all supported KeepUI locales. */\r\nexport type KeepUiLanguage = 'en' | 'es' | 'de';\r\n\r\n/** Ordered list of languages available in KeepUI. */\r\nexport const KEEPUI_AVAILABLE_LANGUAGES: readonly KeepUiLanguage[] = [\r\n 'en',\r\n 'es',\r\n 'de',\r\n] as const;\r\n\r\n","import { Component, ChangeDetectionStrategy, inject, signal } from '@angular/core';\r\nimport { TranslocoPipe, TRANSLOCO_SCOPE } from '@jsverse/transloco';\r\nimport { FILE_PORT } from '../../tokens/file.token';\r\nimport { KEEPUI_TRANSLATION_KEYS as T } from '../../i18n/translation-keys';\r\nimport { ButtonComponent } from '../button/button.component';\r\n\r\n/**\r\n * Standalone component that allows the user to pick and preview an image.\r\n *\r\n * This component is fully platform-agnostic. It delegates file picking to\r\n * whatever `FilePort` implementation is provided via `FILE_PORT`.\r\n *\r\n * UI strings are fully internationalised via Transloco (scope `'keepui'`).\r\n * Call `provideKeepUiI18n()` in your `app.config.ts` and use\r\n * `KeepUiLanguageService.setLanguage(lang)` to change locale at runtime.\r\n *\r\n * Usage:\r\n * ```html\r\n * <keepui-image-preview />\r\n * ```\r\n *\r\n * Prerequisites — register providers in `app.config.ts`:\r\n * - Web: `provideKeepUi()` + `provideKeepUiI18n()`\r\n * - Capacitor: `provideKeepUiCapacitor()` + `provideKeepUiI18n()`\r\n */\r\n@Component({\r\n selector: 'keepui-image-preview',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [TranslocoPipe, ButtonComponent],\r\n providers: [{ provide: TRANSLOCO_SCOPE, useValue: 'keepui' }],\r\n host: { class: 'block' },\r\n template: `\r\n <div class=\"flex flex-col gap-4\">\r\n\r\n <keepui-button\r\n type=\"button\"\r\n variant=\"secondary\"\r\n size=\"auto\"\r\n shape=\"rounded\"\r\n [loading]=\"loading()\"\r\n (clicked)=\"pickImage()\"\r\n >\r\n {{ (loading() ? keys.LOADING : keys.SELECT_IMAGE) | transloco }}\r\n </keepui-button>\r\n\r\n <span role=\"status\" class=\"sr-only\">\r\n @if (imageUrl()) {\r\n {{ keys.PREVIEW_ALT | transloco }}\r\n }\r\n </span>\r\n\r\n @if (imageUrl()) {\r\n <div class=\"max-w-full\">\r\n <img\r\n class=\"max-w-full h-auto rounded border border-ku-secondary-border\"\r\n [src]=\"imageUrl()\"\r\n [attr.alt]=\"keys.PREVIEW_ALT | transloco\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (error()) {\r\n <p class=\"text-ku-red-text text-sm m-0\" role=\"alert\">{{ error() }}</p>\r\n }\r\n\r\n </div>\r\n `,\r\n})\r\nexport class ImagePreviewComponent {\r\n private readonly filePort = inject(FILE_PORT);\r\n\r\n /** Translation key references (typed via KEEPUI_TRANSLATION_KEYS). */\r\n protected readonly keys = T.IMAGE_PREVIEW;\r\n\r\n /** URL of the selected image, ready to bind to `[src]`. */\r\n readonly imageUrl = signal<string | null>(null);\r\n /** Error message if the last pick operation failed. */\r\n readonly error = signal<string | null>(null);\r\n /** True while the pick operation is in progress. */\r\n readonly loading = signal(false);\r\n\r\n async pickImage(): Promise<void> {\r\n this.error.set(null);\r\n this.loading.set(true);\r\n\r\n try {\r\n const result = await this.filePort.pickImage();\r\n this.imageUrl.set(result.dataUrl);\r\n } catch (err) {\r\n this.error.set(\r\n err instanceof Error ? err.message : T.IMAGE_PREVIEW.ERROR_UNEXPECTED,\r\n );\r\n } finally {\r\n this.loading.set(false);\r\n }\r\n }\r\n}\r\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\r\nimport { FILE_PORT } from '../tokens/file.token';\r\nimport { WebFileService } from '../services/web-file.service';\r\n\r\n/**\r\n * Registers KeepUI core providers for a **web** Angular application.\r\n *\r\n * Registers `WebFileService` as the implementation of `FILE_PORT`, enabling\r\n * all KeepUI components to use the browser's native file picker.\r\n *\r\n * Add to `app.config.ts`:\r\n * ```ts\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [provideKeepUi()],\r\n * };\r\n * ```\r\n */\r\nexport function provideKeepUi(): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n { provide: FILE_PORT, useClass: WebFileService },\r\n ]);\r\n}\r\n","/**\r\n * Inline translation objects for KeepUI.\r\n *\r\n * These mirror `en.json`, `es.json` and `de.json` and are bundled directly\r\n * into the library so that no HTTP request is needed at runtime.\r\n *\r\n * > **Do not edit this file manually.**\r\n * > Keep it in sync with the JSON files inside `/i18n/`.\r\n */\r\n\r\nimport { KeepUiLanguage } from './translation-keys';\r\n\r\nexport interface KeepUiTranslationSchema {\r\n imagePreview: {\r\n selectImage: string;\r\n loading: string;\r\n previewAlt: string;\r\n errorUnexpected: string;\r\n };\r\n}\r\n\r\nconst EN: KeepUiTranslationSchema = {\r\n imagePreview: {\r\n selectImage: 'Select image',\r\n loading: 'Loading…',\r\n previewAlt: 'Selected image preview',\r\n errorUnexpected: 'An unexpected error occurred',\r\n },\r\n};\r\n\r\nconst ES: KeepUiTranslationSchema = {\r\n imagePreview: {\r\n selectImage: 'Seleccionar imagen',\r\n loading: 'Cargando…',\r\n previewAlt: 'Vista previa de imagen seleccionada',\r\n errorUnexpected: 'Ha ocurrido un error inesperado',\r\n },\r\n};\r\n\r\nconst DE: KeepUiTranslationSchema = {\r\n imagePreview: {\r\n selectImage: 'Bild auswählen',\r\n loading: 'Laden…',\r\n previewAlt: 'Vorschau des ausgewählten Bildes',\r\n errorUnexpected: 'Ein unerwarteter Fehler ist aufgetreten',\r\n },\r\n};\r\n\r\n/** Map from locale code to its translation object. */\r\nexport const KEEPUI_TRANSLATIONS: Record<KeepUiLanguage, KeepUiTranslationSchema> = {\r\n en: EN,\r\n es: ES,\r\n de: DE,\r\n};\r\n\r\n","import { Injectable } from '@angular/core';\r\nimport { Translation, TranslocoLoader } from '@jsverse/transloco';\r\nimport { Observable, of } from 'rxjs';\r\nimport { KeepUiLanguage } from '../i18n/translation-keys';\r\nimport { KEEPUI_TRANSLATIONS } from '../i18n/keep-ui-translations';\r\n\r\n/**\r\n * Transloco loader for KeepUI.\r\n *\r\n * Serves translations **inline** (bundled in the library JS) so that no HTTP\r\n * request is required. It handles both plain lang paths (`'en'`, `'es'`, …)\r\n * and scope-prefixed paths (`'keepui/en'`, `'keepui/es'`, …).\r\n *\r\n * @internal\r\n */\r\n@Injectable()\r\nexport class KeepUiTranslocoLoader implements TranslocoLoader {\r\n getTranslation(langPath: string): Observable<Translation> {\r\n // Transloco calls this with 'keepui/en', 'keepui/es', etc.\r\n // when a component has TRANSLOCO_SCOPE = 'keepui'.\r\n const lang = langPath.includes('/') ? langPath.split('/')[1] : langPath;\r\n\r\n const isValid = (l: string): l is KeepUiLanguage =>\r\n l === 'en' || l === 'es' || l === 'de';\r\n\r\n const translations = isValid(lang)\r\n ? KEEPUI_TRANSLATIONS[lang]\r\n : KEEPUI_TRANSLATIONS['en'];\r\n\r\n return of(translations as unknown as Translation);\r\n }\r\n}\r\n\r\n","import { Injectable, inject, signal } from '@angular/core';\r\nimport { TranslocoService } from '@jsverse/transloco';\r\nimport { KeepUiLanguage, KEEPUI_AVAILABLE_LANGUAGES } from '../i18n/translation-keys';\r\n\r\n/**\r\n * Service that exposes a public API for changing the active language of all\r\n * KeepUI components at runtime.\r\n *\r\n * ### Usage\r\n * ```ts\r\n * // Inject anywhere in the host application\r\n * const lang = inject(KeepUiLanguageService);\r\n *\r\n * // Switch to Spanish\r\n * lang.setLanguage('es');\r\n *\r\n * // Read the active language\r\n * console.log(lang.activeLanguage()); // 'es'\r\n * ```\r\n *\r\n * Register via `provideKeepUiI18n()` in `app.config.ts`.\r\n */\r\n@Injectable()\r\nexport class KeepUiLanguageService {\r\n private readonly transloco = inject(TranslocoService);\r\n\r\n /** Signal that reflects the currently active KeepUI locale. */\r\n readonly activeLanguage = signal<KeepUiLanguage>(\r\n this.transloco.getActiveLang() as KeepUiLanguage,\r\n );\r\n\r\n /** Ordered list of all supported locales. */\r\n readonly availableLanguages: readonly KeepUiLanguage[] = KEEPUI_AVAILABLE_LANGUAGES;\r\n\r\n /**\r\n * Changes the active language for all KeepUI components.\r\n *\r\n * @param lang - One of `'en'`, `'es'` or `'de'`.\r\n */\r\n setLanguage(lang: KeepUiLanguage): void {\r\n this.transloco.setActiveLang(lang);\r\n this.activeLanguage.set(lang);\r\n }\r\n}\r\n\r\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\r\nimport { provideTransloco } from '@jsverse/transloco';\r\nimport { KeepUiLanguage, KEEPUI_AVAILABLE_LANGUAGES } from '../i18n/translation-keys';\r\nimport { KeepUiTranslocoLoader } from '../services/keep-ui-transloco-loader.service';\r\nimport { KeepUiLanguageService } from '../services/keep-ui-language.service';\r\n\r\n/**\r\n * Options for `provideKeepUiI18n()`.\r\n */\r\nexport interface KeepUiI18nOptions {\r\n /**\r\n * The default language to use when the app starts.\r\n * @default 'en'\r\n */\r\n defaultLang?: KeepUiLanguage;\r\n}\r\n\r\n/**\r\n * Registers all providers required for KeepUI internationalisation.\r\n *\r\n * - Configures a self-contained Transloco instance scoped to `'keepui'`.\r\n * - Bundles all translations **inline** — no HTTP assets required.\r\n * - Provides {@link KeepUiLanguageService} so the host app can switch locale.\r\n *\r\n * ### Usage in `app.config.ts`\r\n * ```ts\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [\r\n * provideKeepUi(),\r\n * provideKeepUiI18n(), // default language: 'en'\r\n * provideKeepUiI18n({ defaultLang: 'es' }), // or start in Spanish\r\n * ],\r\n * };\r\n * ```\r\n *\r\n * ### Changing language at runtime\r\n * ```ts\r\n * const lang = inject(KeepUiLanguageService);\r\n * lang.setLanguage('de');\r\n * ```\r\n *\r\n * > **Note:** If your application already calls `provideTransloco()`, do NOT\r\n * > call `provideKeepUiI18n()` — instead configure your Transloco loader to\r\n * > handle the `'keepui/{lang}'` scope paths, and provide `KeepUiLanguageService`\r\n * > manually.\r\n */\r\nexport function provideKeepUiI18n(\r\n options: KeepUiI18nOptions = {},\r\n): EnvironmentProviders {\r\n const defaultLang: KeepUiLanguage = options.defaultLang ?? 'en';\r\n\r\n return makeEnvironmentProviders([\r\n provideTransloco({\r\n config: {\r\n defaultLang,\r\n availableLangs: [...KEEPUI_AVAILABLE_LANGUAGES],\r\n reRenderOnLangChange: true,\r\n failedRetries: 0,\r\n missingHandler: { useFallbackTranslation: true },\r\n fallbackLang: 'en',\r\n },\r\n loader: KeepUiTranslocoLoader,\r\n }),\r\n\r\n KeepUiLanguageService,\r\n ]);\r\n}\r\n\r\n","import { Injectable } from '@angular/core';\r\nimport { FilePort } from '../ports/file.port';\r\nimport { FileResult } from '../models/file-result.model';\r\n\r\n/**\r\n * A controllable mock implementation of `FilePort` for use in unit tests.\r\n *\r\n * By default, `pickImage()` resolves successfully. Set `nextError` to make the\r\n * next call reject with that error.\r\n *\r\n * ```ts\r\n * providers: [{ provide: FILE_PORT, useClass: MockFileService }]\r\n * ```\r\n */\r\n@Injectable()\r\nexport class MockFileService implements FilePort {\r\n /** Set this to make the next `pickImage()` call reject with this error. */\r\n nextError: Error | null = null;\r\n\r\n pickImage(): Promise<FileResult> {\r\n if (this.nextError) {\r\n const err = this.nextError;\r\n this.nextError = null;\r\n return Promise.reject(err);\r\n }\r\n return Promise.resolve({\r\n dataUrl: 'data:image/jpeg;base64,mockImageData==',\r\n mimeType: 'image/jpeg',\r\n });\r\n }\r\n}\r\n","// Public API Surface of @keepui/ui\r\n\r\n// Models\r\nexport * from './lib/models/file-result.model';\r\n\r\n// Ports (interfaces)\r\nexport * from './lib/ports/file.port';\r\n\r\n// Injection tokens\r\nexport * from './lib/tokens/file.token';\r\n\r\n// Services\r\nexport * from './lib/services/web-file.service';\r\n\r\n// Components\r\nexport * from './lib/components/button/button.types';\r\nexport * from './lib/components/button/button.component';\r\nexport * from './lib/components/card/card.component';\r\nexport * from './lib/components/image-preview/image-preview.component';\r\n\r\n// Providers\r\nexport * from './lib/providers/provide-keep-ui';\r\nexport * from './lib/providers/keep-ui-i18n.provider';\r\n\r\n// i18n – translation keys & language utilities\r\nexport * from './lib/i18n/translation-keys';\r\nexport * from './lib/i18n/keep-ui-translations';\r\nexport * from './lib/services/keep-ui-language.service';\r\n\r\n// Testing utilities\r\nexport * from './lib/testing/mock-file.service';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["T"],"mappings":"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"}
@@ -1,23 +1,48 @@
1
1
  import * as i0 from "@angular/core";
2
+ export type CardVariant = 'flat' | 'outlined';
3
+ export type CardPadding = 'none' | 'sm' | 'md' | 'lg' | 'screen';
4
+ export type CardColors = 'primary' | 'secondary';
2
5
  /**
3
- * A versatile card container component.
6
+ * Contenedor versátil con soporte de variante, padding, color, estado clickable,
7
+ * seleccionado y scrollable.
8
+ *
9
+ * `padding="screen"` aplica padding lateral y superior pero omite el inferior
10
+ * intencionadamente para que el contenido parezca continuar más allá del área visible,
11
+ * invitando al usuario a hacer scroll. Un `div` espaciador se inserta automáticamente
12
+ * al final del contenido proyectado: cuando el usuario llega al fondo, el espaciador
13
+ * reproduce el padding inferior correcto sin que el consumidor deba declararlo.
4
14
  *
5
15
  * ```html
6
- * <keepui-card>
7
- * <h2>Title</h2>
8
- * <p>Some content</p>
9
- * </keepui-card>
16
+ * <keepui-card>Contenido</keepui-card>
10
17
  *
11
- * <keepui-card [elevation]="2">
12
- * Elevated card
18
+ * <keepui-card variant="flat" padding="lg" [clickable]="true" (clicked)="onSelect()">
19
+ * Card clicable
13
20
  * </keepui-card>
21
+ *
22
+ * <div class="h-screen overflow-hidden">
23
+ * <keepui-card padding="screen" [scrollable]="true" [fullHeight]="true">
24
+ * Contenido largo — el padding inferior se gestiona automáticamente
25
+ * </keepui-card>
26
+ * </div>
14
27
  * ```
15
28
  */
16
29
  export declare class CardComponent {
17
- /** Shadow elevation level (0–3). */
18
- readonly elevation: import("@angular/core").InputSignal<0 | 1 | 2 | 3>;
30
+ private readonly BASE_CLASS;
31
+ private readonly VARIANT_CLASS_MAP;
32
+ private readonly SELECTED_VARIANT_CLASS_MAP;
33
+ private readonly PADDING_CLASS_MAP;
34
+ private readonly COLORS_CLASS_MAP;
35
+ readonly variant: import("@angular/core").InputSignal<CardVariant>;
36
+ readonly padding: import("@angular/core").InputSignal<CardPadding>;
37
+ readonly colors: import("@angular/core").InputSignal<CardColors>;
38
+ readonly clickable: import("@angular/core").InputSignal<boolean>;
39
+ readonly selected: import("@angular/core").InputSignal<boolean>;
40
+ readonly scrollable: import("@angular/core").InputSignal<boolean>;
41
+ readonly fullHeight: import("@angular/core").InputSignal<boolean>;
42
+ readonly clicked: import("@angular/core").OutputEmitterRef<void>;
19
43
  protected readonly cardClass: import("@angular/core").Signal<string>;
44
+ protected onCardClick(): void;
20
45
  static ɵfac: i0.ɵɵFactoryDeclaration<CardComponent, never>;
21
- static ɵcmp: i0.ɵɵComponentDeclaration<CardComponent, "keepui-card", never, { "elevation": { "alias": "elevation"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
46
+ static ɵcmp: i0.ɵɵComponentDeclaration<CardComponent, "keepui-card", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "clickable": { "alias": "clickable"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "scrollable": { "alias": "scrollable"; "required": false; "isSignal": true; }; "fullHeight": { "alias": "fullHeight"; "required": false; "isSignal": true; }; }, { "clicked": "clicked"; }, never, ["*"], true, never>;
22
47
  }
23
48
  //# sourceMappingURL=card.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"card.component.d.ts","sourceRoot":"","sources":["../../../../../projects/keep-ui/src/lib/components/card/card.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;GAaG;AACH,qBAUa,aAAa;IACxB,oCAAoC;IACpC,QAAQ,CAAC,SAAS,qDAA2B;IAE7C,SAAS,CAAC,QAAQ,CAAC,SAAS,yCAgBzB;yCApBQ,aAAa;2CAAb,aAAa;CAqBzB"}
1
+ {"version":3,"file":"card.component.d.ts","sourceRoot":"","sources":["../../../../../projects/keep-ui/src/lib/components/card/card.component.ts"],"names":[],"mappings":";AAEA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,CAAC;AAC9C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAwBa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkD;IAE7E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAGhC;IAEF,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAGzC;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAMhC;IAEF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAG/B;IAEF,QAAQ,CAAC,OAAO,mDAAkC;IAClD,QAAQ,CAAC,OAAO,mDAA4B;IAC5C,QAAQ,CAAC,MAAM,kDAAgC;IAC/C,QAAQ,CAAC,SAAS,+CAAyB;IAC3C,QAAQ,CAAC,QAAQ,+CAAyB;IAC1C,QAAQ,CAAC,UAAU,+CAAyB;IAC5C,QAAQ,CAAC,UAAU,+CAAyB;IAC5C,QAAQ,CAAC,OAAO,iDAAkB;IAElC,SAAS,CAAC,QAAQ,CAAC,SAAS,yCA2BzB;IAEH,SAAS,CAAC,WAAW,IAAI,IAAI;yCAhElB,aAAa;2CAAb,aAAa;CAqEzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keepui/ui",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "KeepUI — Angular cross-platform UI component library with optional Capacitor support",
5
5
  "keywords": [
6
6
  "angular",
package/styles/index.css CHANGED
@@ -14,23 +14,32 @@
14
14
  * ─── Tailwind v4 utilities generated ────────────────────────────────────────
15
15
  *
16
16
  * Surface & background:
17
- * bg-keepui-background bg-keepui-surface bg-keepui-surface-hover
17
+ * bg-ku-primary bg-ku-secondary bg-ku-secondary-hover
18
18
  *
19
19
  * Text:
20
- * text-keepui-text text-keepui-text-muted text-keepui-text-disabled
20
+ * text-ku-primary-text text-ku-secondary-text text-ku-gray-text
21
21
  *
22
22
  * Borders:
23
- * border-keepui-border border-keepui-border-strong
23
+ * border-ku-secondary-border border-ku-primary-border border-ku-gray-border
24
24
  *
25
- * Brand / Primary:
26
- * bg-keepui-primary bg-keepui-primary-hover bg-keepui-primary-active
27
- * text-keepui-primary-fg border-keepui-primary
25
+ * Brand / Accent:
26
+ * text-ku-brand-text border-ku-primary-border bg-ku-primary/10
28
27
  *
29
28
  * Feedback:
30
- * text-keepui-error bg-keepui-error bg-keepui-success bg-keepui-warning
29
+ * text-ku-red-text bg-ku-red-bg border-ku-red-border text-ku-green-text
31
30
  *
32
- * Shadows:
33
- * shadow-keepui-sm shadow-keepui-md shadow-keepui-lg
31
+ * Semantic color palette (bg / border / text per color):
32
+ * bg-ku-gray-bg border-ku-gray-border text-ku-gray-text
33
+ * bg-ku-brand-bg border-ku-brand-border text-ku-brand-text
34
+ * bg-ku-indigo-bg border-ku-indigo-border text-ku-indigo-text
35
+ * bg-ku-blue-bg border-ku-blue-border text-ku-blue-text
36
+ * bg-ku-pink-bg border-ku-pink-border text-ku-pink-text
37
+ * bg-ku-orange-bg border-ku-orange-border text-ku-orange-text
38
+ * bg-ku-yellow-bg border-ku-yellow-border text-ku-yellow-text
39
+ * bg-ku-green-bg border-ku-green-border text-ku-green-text
40
+ * bg-ku-purple-bg border-ku-purple-border text-ku-purple-text
41
+ * bg-ku-teal-bg border-ku-teal-border text-ku-teal-text
42
+ * bg-ku-red-bg border-ku-red-border text-ku-red-text
34
43
  *
35
44
  * ─── Requirements ───────────────────────────────────────────────────────────
36
45
  *
@@ -48,37 +57,64 @@
48
57
  * @source inline() declares all Tailwind classes used by KeepUI components
49
58
  * explicitly — the string must be on a SINGLE LINE (CSS string literal rule).
50
59
  */
51
- @source inline("inline-flex items-center justify-center gap-2 min-h-[2.75rem] text-sm font-medium cursor-pointer select-none transition-colors duration-200 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-keepui-primary focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed w-full h-10 px-4 w-40 px-6 rounded-full rounded-2xl rounded-lg bg-keepui-primary text-keepui-primary-fg border border-keepui-primary enabled:hover:bg-keepui-primary-hover enabled:hover:border-keepui-primary-hover enabled:active:bg-keepui-primary-active enabled:active:border-keepui-primary-active bg-keepui-surface text-keepui-text border-keepui-border enabled:hover:bg-keepui-surface-hover enabled:hover:border-keepui-border-strong disabled:text-keepui-text-disabled bg-transparent text-keepui-primary enabled:hover:border-keepui-primary enabled:hover:text-keepui-primary bg-keepui-primary/10 enabled:hover:bg-keepui-primary/10 bg-keepui-error text-keepui-error-fg border-keepui-error enabled:hover:opacity-90 enabled:active:opacity-80 p-4 shadow-keepui-sm shadow-keepui-md shadow-keepui-lg flex flex-col gap-4 max-w-full h-auto text-keepui-error m-0 block sr-only");
60
+ @source inline("inline-flex items-center justify-center gap-2 min-h-[2.75rem] text-sm font-medium cursor-pointer select-none transition-colors duration-200 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ku-primary-border focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed w-full h-10 px-4 w-40 px-6 rounded-full rounded-2xl rounded-lg bg-ku-primary text-ku-primary-text border border-ku-primary-border enabled:hover:bg-ku-primary-hover enabled:hover:border-ku-primary-border enabled:active:bg-ku-primary-hover enabled:active:border-ku-primary-border bg-ku-secondary text-ku-secondary-text border-ku-secondary-border enabled:hover:bg-ku-secondary-hover enabled:hover:border-ku-gray-border disabled:text-ku-gray-text bg-transparent text-ku-brand-text enabled:hover:border-ku-primary-border enabled:hover:text-ku-brand-text bg-ku-primary/10 enabled:hover:bg-ku-primary/10 bg-ku-red-bg text-ku-red-text border-ku-red-border enabled:hover:opacity-90 enabled:active:opacity-80 p-4 shadow-sm shadow shadow-lg flex flex-col gap-4 max-w-full h-auto m-0 block sr-only bg-ku-gray-bg border-ku-gray-border text-ku-gray-text bg-ku-brand-bg border-ku-brand-border text-ku-brand-text bg-ku-indigo-bg border-ku-indigo-border text-ku-indigo-text bg-ku-blue-bg border-ku-blue-border text-ku-blue-text bg-ku-pink-bg border-ku-pink-border text-ku-pink-text bg-ku-orange-bg border-ku-orange-border text-ku-orange-text bg-ku-yellow-bg border-ku-yellow-border text-ku-yellow-text bg-ku-green-bg border-ku-green-border text-ku-green-text bg-ku-purple-bg border-ku-purple-border text-ku-purple-text bg-ku-teal-bg border-ku-teal-border text-ku-teal-text bg-ku-red-bg border-ku-red-border text-ku-red-text rounded-xl overflow-hidden transition-colors border-ku-brand-border hover:bg-ku-primary-hover focus:outline-none focus-visible:ring-ku-brand-border h-full h-4 shrink-0 overflow-y-auto overflow-x-auto p-3 p-6 px-4 pt-4");
52
61
 
53
62
  @theme inline {
54
- /* ── Surfaces ── */
55
- --color-keepui-background: var(--keepui-background);
56
- --color-keepui-surface: var(--keepui-surface);
57
- --color-keepui-surface-hover: var(--keepui-surface-hover);
58
-
59
- /* ── Borders ── */
60
- --color-keepui-border: var(--keepui-border);
61
- --color-keepui-border-strong: var(--keepui-border-strong);
62
-
63
- /* ── Text ── */
64
- --color-keepui-text: var(--keepui-text);
65
- --color-keepui-text-muted: var(--keepui-text-muted);
66
- --color-keepui-text-disabled: var(--keepui-text-disabled);
67
-
68
- /* ── Primary ── */
69
- --color-keepui-primary: var(--keepui-primary);
70
- --color-keepui-primary-hover: var(--keepui-primary-hover);
71
- --color-keepui-primary-active: var(--keepui-primary-active);
72
- --color-keepui-primary-fg: var(--keepui-primary-foreground);
73
-
74
- /* ── Feedback ── */
75
- --color-keepui-error: var(--keepui-error);
76
- --color-keepui-error-fg: var(--keepui-error-foreground);
77
- --color-keepui-success: var(--keepui-success);
78
- --color-keepui-warning: var(--keepui-warning);
79
-
80
- /* ── Shadows ── */
81
- --shadow-keepui-sm: var(--keepui-shadow-sm);
82
- --shadow-keepui-md: var(--keepui-shadow-md);
83
- --shadow-keepui-lg: var(--keepui-shadow-lg);
63
+
64
+ /* ── Primary surface ── */
65
+ --color-ku-primary: var(--ku-primary);
66
+ --color-ku-primary-hover: var(--ku-primary-hover);
67
+ --color-ku-primary-text: var(--ku-primary-text);
68
+ --color-ku-primary-border: var(--ku-primary-border);
69
+
70
+ /* ── Secondary surface ── */
71
+ --color-ku-secondary: var(--ku-secondary);
72
+ --color-ku-secondary-hover: var(--ku-secondary-hover);
73
+ --color-ku-secondary-text: var(--ku-secondary-text);
74
+ --color-ku-secondary-border: var(--ku-secondary-border);
75
+
76
+ /* ── Semantic color palette ── */
77
+ --color-ku-gray-bg: var(--ku-gray-bg);
78
+ --color-ku-gray-border: var(--ku-gray-border);
79
+ --color-ku-gray-text: var(--ku-gray-text);
80
+
81
+ --color-ku-brand-bg: var(--ku-brand-bg);
82
+ --color-ku-brand-border: var(--ku-brand-border);
83
+ --color-ku-brand-text: var(--ku-brand-text);
84
+
85
+ --color-ku-indigo-bg: var(--ku-indigo-bg);
86
+ --color-ku-indigo-border: var(--ku-indigo-border);
87
+ --color-ku-indigo-text: var(--ku-indigo-text);
88
+
89
+ --color-ku-blue-bg: var(--ku-blue-bg);
90
+ --color-ku-blue-border: var(--ku-blue-border);
91
+ --color-ku-blue-text: var(--ku-blue-text);
92
+
93
+ --color-ku-pink-bg: var(--ku-pink-bg);
94
+ --color-ku-pink-border: var(--ku-pink-border);
95
+ --color-ku-pink-text: var(--ku-pink-text);
96
+
97
+ --color-ku-orange-bg: var(--ku-orange-bg);
98
+ --color-ku-orange-border: var(--ku-orange-border);
99
+ --color-ku-orange-text: var(--ku-orange-text);
100
+
101
+ --color-ku-yellow-bg: var(--ku-yellow-bg);
102
+ --color-ku-yellow-border: var(--ku-yellow-border);
103
+ --color-ku-yellow-text: var(--ku-yellow-text);
104
+
105
+ --color-ku-green-bg: var(--ku-green-bg);
106
+ --color-ku-green-border: var(--ku-green-border);
107
+ --color-ku-green-text: var(--ku-green-text);
108
+
109
+ --color-ku-purple-bg: var(--ku-purple-bg);
110
+ --color-ku-purple-border: var(--ku-purple-border);
111
+ --color-ku-purple-text: var(--ku-purple-text);
112
+
113
+ --color-ku-teal-bg: var(--ku-teal-bg);
114
+ --color-ku-teal-border: var(--ku-teal-border);
115
+ --color-ku-teal-text: var(--ku-teal-text);
116
+
117
+ --color-ku-red-bg: var(--ku-red-bg);
118
+ --color-ku-red-border: var(--ku-red-border);
119
+ --color-ku-red-text: var(--ku-red-text);
84
120
  }
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.fixed{position:fixed}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.left-0{left:calc(var(--spacing) * 0)}.z-30{z-index:30}.z-40{z-index:40}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.-mx-2{margin-inline:calc(var(--spacing) * -2)}.mx-auto{margin-inline:auto}.mt-0{margin-top:calc(var(--spacing) * 0)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mb-0{margin-bottom:calc(var(--spacing) * 0)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-14{height:calc(var(--spacing) * 14)}.h-auto{height:auto}.h-screen{height:100vh}.min-h-\[2\.75rem\]{min-height:2.75rem}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-40{width:calc(var(--spacing) * 40)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-full{max-width:100%}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[2\.75rem\]{min-width:2.75rem}.min-w-max{min-width:max-content}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-8{gap:calc(var(--spacing) * 8)}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-keepui-border{border-color:var(--keepui-border)}.border-keepui-border-strong{border-color:var(--keepui-border-strong)}.border-keepui-error{border-color:var(--keepui-error)}.border-keepui-primary{border-color:var(--keepui-primary)}.\!bg-keepui-primary{background-color:var(--keepui-primary)!important}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab, red, red)){.bg-black\/50{background-color:color-mix(in oklab, var(--color-black) 50%, transparent)}}.bg-keepui-background{background-color:var(--keepui-background)}.bg-keepui-error{background-color:var(--keepui-error)}.bg-keepui-primary{background-color:var(--keepui-primary)}.bg-keepui-primary-active{background-color:var(--keepui-primary-active)}.bg-keepui-primary-hover{background-color:var(--keepui-primary-hover)}.bg-keepui-success{background-color:var(--keepui-success)}.bg-keepui-surface{background-color:var(--keepui-surface)}.bg-keepui-surface-hover{background-color:var(--keepui-surface-hover)}.bg-keepui-warning{background-color:var(--keepui-warning)}.bg-transparent{background-color:#0000}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-2{padding-block:calc(var(--spacing) * 2)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.\!text-keepui-primary-fg{color:var(--keepui-primary-foreground)!important}.text-keepui-error{color:var(--keepui-error)}.text-keepui-error-fg{color:var(--keepui-error-foreground)}.text-keepui-primary{color:var(--keepui-primary)}.text-keepui-primary-fg{color:var(--keepui-primary-foreground)}.text-keepui-success{color:var(--keepui-success)}.text-keepui-text{color:var(--keepui-text)}.text-keepui-text-disabled{color:var(--keepui-text-disabled)}.text-keepui-text-muted{color:var(--keepui-text-muted)}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-keepui-lg{--tw-shadow:var(--keepui-shadow-lg);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-keepui-md{--tw-shadow:var(--keepui-shadow-md);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-keepui-sm{--tw-shadow:var(--keepui-shadow-sm);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:gap-2:is(:where(.group):hover *){gap:calc(var(--spacing) * 2)}}.first\:mt-1\.5:first-child{margin-top:calc(var(--spacing) * 1.5)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:border-keepui-primary:hover{border-color:var(--keepui-primary)}.hover\:bg-keepui-surface-hover:hover{background-color:var(--keepui-surface-hover)}.hover\:text-keepui-text:hover{color:var(--keepui-text)}.hover\:shadow-keepui-sm:hover{--tw-shadow:var(--keepui-shadow-sm);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-keepui-primary:focus-visible{--tw-ring-color:var(--keepui-primary)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}@media (hover:hover){.enabled\:hover\:border-keepui-border-strong:enabled:hover{border-color:var(--keepui-border-strong)}.enabled\:hover\:border-keepui-primary:enabled:hover{border-color:var(--keepui-primary)}.enabled\:hover\:border-keepui-primary-hover:enabled:hover{border-color:var(--keepui-primary-hover)}.enabled\:hover\:bg-keepui-primary-hover:enabled:hover{background-color:var(--keepui-primary-hover)}.enabled\:hover\:bg-keepui-primary\/10:enabled:hover{background-color:var(--keepui-primary)}@supports (color:color-mix(in lab, red, red)){.enabled\:hover\:bg-keepui-primary\/10:enabled:hover{background-color:color-mix(in oklab, var(--keepui-primary) 10%, transparent)}}.enabled\:hover\:bg-keepui-surface-hover:enabled:hover{background-color:var(--keepui-surface-hover)}.enabled\:hover\:text-keepui-primary:enabled:hover{color:var(--keepui-primary)}.enabled\:hover\:opacity-90:enabled:hover{opacity:.9}}.enabled\:active\:border-keepui-primary-active:enabled:active{border-color:var(--keepui-primary-active)}.enabled\:active\:bg-keepui-primary-active:enabled:active{background-color:var(--keepui-primary-active)}.enabled\:active\:opacity-80:enabled:active{opacity:.8}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:text-keepui-text-disabled:disabled{color:var(--keepui-text-disabled)}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:48rem){.md\:static{position:static}.md\:inset-auto{inset:auto}.md\:z-auto{z-index:auto}.md\:hidden{display:none}.md\:translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:p-10{padding:calc(var(--spacing) * 10)}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (prefers-color-scheme:dark){.dark\:bg-keepui-primary{background-color:var(--keepui-primary)}}}:root{--keepui-primary:#3b82f6;--keepui-primary-hover:#2563eb;--keepui-primary-active:#1d4ed8;--keepui-primary-foreground:#fff;--keepui-background:#f5f5f5;--keepui-surface:#fff;--keepui-surface-hover:#f0f0f0;--keepui-border:#e0e0e0;--keepui-border-strong:#ccc;--keepui-text:#1f2937;--keepui-text-muted:#6b7280;--keepui-text-disabled:#9ca3af;--keepui-error:#dc2626;--keepui-error-foreground:#fff;--keepui-success:#16a34a;--keepui-warning:#f59e0b;--keepui-radius:.5rem;--keepui-radius-sm:.25rem;--keepui-shadow-sm:0 1px 3px #0000001f;--keepui-shadow-md:0 3px 6px #00000026;--keepui-shadow-lg:0 6px 12px #0000002e;--keepui-transition:.2s ease;--keepui-disabled-opacity:.5;--keepui-font-family:system-ui, -apple-system, sans-serif}[data-theme=dark],.dark{--keepui-primary:#60a5fa;--keepui-primary-hover:#93c5fd;--keepui-primary-active:#3b82f6;--keepui-primary-foreground:#0f172a;--keepui-background:#0f172a;--keepui-surface:#1e293b;--keepui-surface-hover:#334155;--keepui-border:#334155;--keepui-border-strong:#475569;--keepui-text:#f1f5f9;--keepui-text-muted:#94a3b8;--keepui-text-disabled:#64748b;--keepui-error:#f87171;--keepui-error-foreground:#0f172a;--keepui-success:#4ade80;--keepui-warning:#fbbf24;--keepui-shadow-sm:0 1px 3px #0006;--keepui-shadow-md:0 3px 6px #00000073;--keepui-shadow-lg:0 6px 12px #00000080}@media (prefers-color-scheme:dark){:root:not([data-theme=light]):not(.light){--keepui-primary:#60a5fa;--keepui-primary-hover:#93c5fd;--keepui-primary-active:#3b82f6;--keepui-primary-foreground:#0f172a;--keepui-background:#0f172a;--keepui-surface:#1e293b;--keepui-surface-hover:#334155;--keepui-border:#334155;--keepui-border-strong:#475569;--keepui-text:#f1f5f9;--keepui-text-muted:#94a3b8;--keepui-text-disabled:#64748b;--keepui-error:#f87171;--keepui-error-foreground:#0f172a;--keepui-success:#4ade80;--keepui-warning:#fbbf24;--keepui-shadow-sm:0 1px 3px #0006;--keepui-shadow-md:0 3px 6px #00000073;--keepui-shadow-lg:0 6px 12px #00000080}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.fixed{position:fixed}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.left-0{left:calc(var(--spacing) * 0)}.z-30{z-index:30}.z-40{z-index:40}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.-mx-2{margin-inline:calc(var(--spacing) * -2)}.mx-auto{margin-inline:auto}.mt-0{margin-top:calc(var(--spacing) * 0)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mb-0{margin-bottom:calc(var(--spacing) * 0)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-4{height:calc(var(--spacing) * 4)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-14{height:calc(var(--spacing) * 14)}.h-52{height:calc(var(--spacing) * 52)}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.min-h-\[2\.75rem\]{min-height:2.75rem}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-40{width:calc(var(--spacing) * 40)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-full{max-width:100%}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[2\.75rem\]{min-width:2.75rem}.min-w-max{min-width:max-content}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-8{gap:calc(var(--spacing) * 8)}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-ku-blue-border{border-color:var(--ku-blue-border)}.border-ku-brand-border{border-color:var(--ku-brand-border)}.border-ku-gray-border{border-color:var(--ku-gray-border)}.border-ku-green-border{border-color:var(--ku-green-border)}.border-ku-indigo-border{border-color:var(--ku-indigo-border)}.border-ku-orange-border{border-color:var(--ku-orange-border)}.border-ku-pink-border{border-color:var(--ku-pink-border)}.border-ku-primary-border{border-color:var(--ku-primary-border)}.border-ku-purple-border{border-color:var(--ku-purple-border)}.border-ku-red-border{border-color:var(--ku-red-border)}.border-ku-secondary-border{border-color:var(--ku-secondary-border)}.border-ku-teal-border{border-color:var(--ku-teal-border)}.border-ku-yellow-border{border-color:var(--ku-yellow-border)}.\!bg-ku-primary-hover{background-color:var(--ku-primary-hover)!important}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab, red, red)){.bg-black\/50{background-color:color-mix(in oklab, var(--color-black) 50%, transparent)}}.bg-ku-blue-bg{background-color:var(--ku-blue-bg)}.bg-ku-brand-bg{background-color:var(--ku-brand-bg)}.bg-ku-gray-bg{background-color:var(--ku-gray-bg)}.bg-ku-green-bg{background-color:var(--ku-green-bg)}.bg-ku-indigo-bg{background-color:var(--ku-indigo-bg)}.bg-ku-orange-bg{background-color:var(--ku-orange-bg)}.bg-ku-pink-bg{background-color:var(--ku-pink-bg)}.bg-ku-primary{background-color:var(--ku-primary)}.bg-ku-primary-hover{background-color:var(--ku-primary-hover)}.bg-ku-purple-bg{background-color:var(--ku-purple-bg)}.bg-ku-red-bg{background-color:var(--ku-red-bg)}.bg-ku-secondary{background-color:var(--ku-secondary)}.bg-ku-secondary-hover{background-color:var(--ku-secondary-hover)}.bg-ku-teal-bg{background-color:var(--ku-teal-bg)}.bg-ku-yellow-bg{background-color:var(--ku-yellow-bg)}.bg-transparent{background-color:#0000}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.\!text-ku-primary-text{color:var(--ku-primary-text)!important}.text-ku-blue-text{color:var(--ku-blue-text)}.text-ku-brand-text{color:var(--ku-brand-text)}.text-ku-gray-text{color:var(--ku-gray-text)}.text-ku-green-text{color:var(--ku-green-text)}.text-ku-indigo-text{color:var(--ku-indigo-text)}.text-ku-orange-text{color:var(--ku-orange-text)}.text-ku-pink-text{color:var(--ku-pink-text)}.text-ku-primary-text{color:var(--ku-primary-text)}.text-ku-purple-text{color:var(--ku-purple-text)}.text-ku-red-text{color:var(--ku-red-text)}.text-ku-secondary-text{color:var(--ku-secondary-text)}.text-ku-teal-text{color:var(--ku-teal-text)}.text-ku-yellow-text{color:var(--ku-yellow-text)}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:gap-2:is(:where(.group):hover *){gap:calc(var(--spacing) * 2)}}.first\:mt-1\.5:first-child{margin-top:calc(var(--spacing) * 1.5)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:border-ku-primary-border:hover{border-color:var(--ku-primary-border)}.hover\:bg-ku-primary-hover:hover{background-color:var(--ku-primary-hover)}.hover\:bg-ku-secondary-hover:hover{background-color:var(--ku-secondary-hover)}.hover\:text-ku-primary-text:hover{color:var(--ku-primary-text)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ku-brand-border:focus-visible{--tw-ring-color:var(--ku-brand-border)}.focus-visible\:ring-ku-primary-border:focus-visible{--tw-ring-color:var(--ku-primary-border)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}@media (hover:hover){.enabled\:hover\:border-ku-gray-border:enabled:hover{border-color:var(--ku-gray-border)}.enabled\:hover\:border-ku-primary-border:enabled:hover{border-color:var(--ku-primary-border)}.enabled\:hover\:bg-ku-primary-hover:enabled:hover{background-color:var(--ku-primary-hover)}.enabled\:hover\:bg-ku-primary\/10:enabled:hover{background-color:var(--ku-primary)}@supports (color:color-mix(in lab, red, red)){.enabled\:hover\:bg-ku-primary\/10:enabled:hover{background-color:color-mix(in oklab, var(--ku-primary) 10%, transparent)}}.enabled\:hover\:bg-ku-secondary-hover:enabled:hover{background-color:var(--ku-secondary-hover)}.enabled\:hover\:text-ku-brand-text:enabled:hover{color:var(--ku-brand-text)}.enabled\:hover\:opacity-90:enabled:hover{opacity:.9}}.enabled\:active\:border-ku-primary-border:enabled:active{border-color:var(--ku-primary-border)}.enabled\:active\:bg-ku-primary-hover:enabled:active{background-color:var(--ku-primary-hover)}.enabled\:active\:opacity-80:enabled:active{opacity:.8}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:text-ku-gray-text:disabled{color:var(--ku-gray-text)}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:48rem){.md\:static{position:static}.md\:inset-auto{inset:auto}.md\:z-auto{z-index:auto}.md\:hidden{display:none}.md\:translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:p-10{padding:calc(var(--spacing) * 10)}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}:root{--ku-primary:#f9f9f9;--ku-primary-hover:#e2e3e8;--ku-primary-text:#171717;--ku-primary-border:#e2e3e8;--ku-secondary:#fff;--ku-secondary-hover:#f9f9f9;--ku-secondary-text:#171717;--ku-secondary-border:#e2e3e8;--ku-gray-bg:#f5f5f5;--ku-gray-border:#d5d7da;--ku-gray-text:#6b7280;--ku-brand-bg:#f9f5ff;--ku-brand-border:#d6bbfb;--ku-brand-text:#6941c6;--ku-indigo-bg:#eef4ff;--ku-indigo-border:#c7d7fe;--ku-indigo-text:#3538cd;--ku-blue-bg:#eff8ff;--ku-blue-border:#b2ddff;--ku-blue-text:#175cd3;--ku-pink-bg:#fdf2fa;--ku-pink-border:#fcceee;--ku-pink-text:#c11574;--ku-orange-bg:#fff6ed;--ku-orange-border:#f9dbaf;--ku-orange-text:#b93815;--ku-yellow-bg:#fefbe8;--ku-yellow-border:#fedf89;--ku-yellow-text:#a15c07;--ku-green-bg:#ecfdf3;--ku-green-border:#abefc6;--ku-green-text:#067647;--ku-purple-bg:#f4f3ff;--ku-purple-border:#d9d6fe;--ku-purple-text:#5925dc;--ku-teal-bg:#f0fdfa;--ku-teal-border:#99f6e4;--ku-teal-text:#0f766e;--ku-red-bg:#fff1f2;--ku-red-border:#fecdd3;--ku-red-text:#b91c1c}[data-theme=dark],.dark{--ku-primary:#050505;--ku-primary-hover:#2c2d31;--ku-primary-text:#fff;--ku-primary-border:#2c2d31;--ku-secondary:#15161a;--ku-secondary-hover:#2c2d31;--ku-secondary-text:#fff;--ku-secondary-border:#2c2d31;--ku-gray-bg:#1f2937;--ku-gray-border:#4b5563;--ku-gray-text:#9ca3af;--ku-brand-bg:#2e1065;--ku-brand-border:#7c3aed;--ku-brand-text:#c4b5fd;--ku-indigo-bg:#1e1b4b;--ku-indigo-border:#4338ca;--ku-indigo-text:#a5b4fc;--ku-blue-bg:#0c1d3e;--ku-blue-border:#2563eb;--ku-blue-text:#93c5fd;--ku-pink-bg:#3d0b2f;--ku-pink-border:#db2777;--ku-pink-text:#f9a8d4;--ku-orange-bg:#2c1204;--ku-orange-border:#ea580c;--ku-orange-text:#fdba74;--ku-yellow-bg:#2c1f02;--ku-yellow-border:#ca8a04;--ku-yellow-text:#fde047;--ku-green-bg:#052e16;--ku-green-border:#16a34a;--ku-green-text:#4ade80;--ku-purple-bg:#1c1040;--ku-purple-border:#6d28d9;--ku-purple-text:#c4b5fd;--ku-teal-bg:#042a28;--ku-teal-border:#0d9488;--ku-teal-text:#2dd4bf;--ku-red-bg:#2d0b0b;--ku-red-border:#dc2626;--ku-red-text:#fca5a5;--spacing-xs:4px;--spacing-sm:8px;--spacing-md:16px;--spacing-lg:24px;--spacing-xl:32px;--spacing-2xl:48px;--spacing-3xl:64px;--spacing-4xl:96px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:24px;--radius-full:9999px;--shadow-sm:0 1px 2px 0 #0000000d;--shadow:1px 1px 12px 0 #0006;--shadow-md:0 4px 6px -1px #0000001a, 0 2px 4px -1px #0000000f;--shadow-lg:0 10px 15px -3px #0000001a, 0 4px 6px -2px #0000000d;--shadow-xl:0 20px 25px -5px #0000001a, 0 10px 10px -5px #0000000a;--shadow-2xl:0 25px 50px -12px #00000040;--z-index-base:0;--z-index-dropdown:1000;--z-index-sticky:1020;--z-index-fixed:1030;--z-index-modal-backdrop:1040;--z-index-modal:1050;--z-index-popover:1060;--z-index-tooltip:1070}@media (prefers-color-scheme:dark){:root:not([data-theme=light]):not(.light){--ku-gray-bg:#1f2937;--ku-gray-border:#4b5563;--ku-gray-text:#9ca3af;--ku-brand-bg:#2e1065;--ku-brand-border:#7c3aed;--ku-brand-text:#c4b5fd;--ku-indigo-bg:#1e1b4b;--ku-indigo-border:#4338ca;--ku-indigo-text:#a5b4fc;--ku-blue-bg:#0c1d3e;--ku-blue-border:#2563eb;--ku-blue-text:#93c5fd;--ku-pink-bg:#3d0b2f;--ku-pink-border:#db2777;--ku-pink-text:#f9a8d4;--ku-orange-bg:#2c1204;--ku-orange-border:#ea580c;--ku-orange-text:#fdba74;--ku-yellow-bg:#2c1f02;--ku-yellow-border:#ca8a04;--ku-yellow-text:#fde047;--ku-green-bg:#052e16;--ku-green-border:#16a34a;--ku-green-text:#4ade80;--ku-purple-bg:#1c1040;--ku-purple-border:#6d28d9;--ku-purple-text:#c4b5fd;--ku-teal-bg:#042a28;--ku-teal-border:#0d9488;--ku-teal-text:#2dd4bf;--ku-red-bg:#2d0b0b;--ku-red-border:#dc2626;--ku-red-text:#fca5a5}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
package/styles/themes.css CHANGED
@@ -1,132 +1,214 @@
1
- /*
2
- * KeepUI — Default Theme
3
- *
4
- * This file defines all the CSS custom properties (design tokens) used by
5
- * KeepUI components. Import it once in your application's global stylesheet:
6
- *
7
- * @import "@keepui/ui/styles/themes.css";
8
- *
9
- * ─── How to customise ───────────────────────────────────────────────────
10
- *
11
- * Override any variable in your own CSS to create a custom theme:
12
- *
13
- * :root {
14
- * --keepui-primary: #6366f1;
15
- * --keepui-primary-hover: #4f46e5;
16
- * }
17
- *
18
- * [data-theme="dark"] {
19
- * --keepui-primary: #818cf8;
20
- * --keepui-primary-hover: #a5b4fc;
21
- * }
22
- *
23
- * ─── Switching themes at runtime ────────────────────────────────────────
24
- *
25
- * Add `data-theme="dark"` to <html> or any ancestor element:
26
- *
27
- * document.documentElement.setAttribute('data-theme', 'dark');
28
- *
29
- * Or rely on the user's OS preference — the defaults already include
30
- * a `@media (prefers-color-scheme: dark)` block.
31
- * ────────────────────────────────────────────────────────────────────────
32
- */
33
-
34
1
  /* ===== LIGHT THEME (default) ===== */
35
2
  :root {
36
- /* ── Brand ── */
37
- --keepui-primary: #3b82f6;
38
- --keepui-primary-hover: #2563eb;
39
- --keepui-primary-active: #1d4ed8;
40
- --keepui-primary-foreground: #ffffff;
41
-
42
- /* ── Surfaces ── */
43
- --keepui-background: #f5f5f5;
44
- --keepui-surface: #ffffff;
45
- --keepui-surface-hover: #f0f0f0;
46
-
47
- /* ── Borders ── */
48
- --keepui-border: #e0e0e0;
49
- --keepui-border-strong: #cccccc;
50
-
51
- /* ── Text ── */
52
- --keepui-text: #1f2937;
53
- --keepui-text-muted: #6b7280;
54
- --keepui-text-disabled: #9ca3af;
55
-
56
- /* ── Feedback ── */
57
- --keepui-error: #dc2626;
58
- --keepui-error-foreground: #ffffff;
59
- --keepui-success: #16a34a;
60
- --keepui-warning: #f59e0b;
61
-
62
- /* ── Misc ── */
63
- --keepui-radius: 0.5rem;
64
- --keepui-radius-sm: 0.25rem;
65
- --keepui-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12);
66
- --keepui-shadow-md: 0 3px 6px rgba(0, 0, 0, 0.15);
67
- --keepui-shadow-lg: 0 6px 12px rgba(0, 0, 0, 0.18);
68
- --keepui-transition: 0.2s ease;
69
- --keepui-disabled-opacity: 0.5;
70
- --keepui-font-family: system-ui, -apple-system, sans-serif;
3
+ --ku-primary: #F9F9F9;
4
+ --ku-primary-hover: #E2E3E8;
5
+ --ku-primary-text: #171717;
6
+ --ku-primary-border: #E2E3E8;
7
+
8
+ --ku-secondary: #FFFFFF;
9
+ --ku-secondary-hover: #F9F9F9;
10
+ --ku-secondary-text: #171717;
11
+ --ku-secondary-border: #E2E3E8;
12
+
13
+ --ku-gray-bg: #F5F5F5;
14
+ --ku-gray-border: #D5D7DA;
15
+ --ku-gray-text: #6B7280;
16
+
17
+ --ku-brand-bg: #F9F5FF;
18
+ --ku-brand-border: #D6BBFB;
19
+ --ku-brand-text: #6941C6;
20
+
21
+ --ku-indigo-bg: #EEF4FF;
22
+ --ku-indigo-border: #C7D7FE;
23
+ --ku-indigo-text: #3538CD;
24
+
25
+ --ku-blue-bg: #EFF8FF;
26
+ --ku-blue-border: #B2DDFF;
27
+ --ku-blue-text: #175CD3;
28
+
29
+ --ku-pink-bg: #FDF2FA;
30
+ --ku-pink-border: #FCCEEE;
31
+ --ku-pink-text: #C11574;
32
+
33
+ --ku-orange-bg: #FFF6ED;
34
+ --ku-orange-border: #F9DBAF;
35
+ --ku-orange-text: #B93815;
36
+
37
+ --ku-yellow-bg: #FEFBE8;
38
+ --ku-yellow-border: #FEDF89;
39
+ --ku-yellow-text: #A15C07;
40
+
41
+ --ku-green-bg: #ECFDF3;
42
+ --ku-green-border: #ABEFC6;
43
+ --ku-green-text: #067647;
44
+
45
+ --ku-purple-bg: #F4F3FF;
46
+ --ku-purple-border: #D9D6FE;
47
+ --ku-purple-text: #5925DC;
48
+
49
+ --ku-teal-bg: #F0FDFA;
50
+ --ku-teal-border: #99F6E4;
51
+ --ku-teal-text: #0F766E;
52
+
53
+ --ku-red-bg: #FFF1F2;
54
+ --ku-red-border: #FECDD3;
55
+ --ku-red-text: #B91C1C;
56
+
71
57
  }
72
58
 
73
59
  /* ===== DARK THEME ===== */
74
- /* Soporta tanto clase .dark (Tailwind estándar) como atributo data-theme="dark" */
75
60
  [data-theme="dark"],
76
61
  .dark {
77
- --keepui-primary: #60a5fa;
78
- --keepui-primary-hover: #93c5fd;
79
- --keepui-primary-active: #3b82f6;
80
- --keepui-primary-foreground: #0f172a;
81
-
82
- --keepui-background: #0f172a;
83
- --keepui-surface: #1e293b;
84
- --keepui-surface-hover: #334155;
85
-
86
- --keepui-border: #334155;
87
- --keepui-border-strong: #475569;
88
-
89
- --keepui-text: #f1f5f9;
90
- --keepui-text-muted: #94a3b8;
91
- --keepui-text-disabled: #64748b;
92
-
93
- --keepui-error: #f87171;
94
- --keepui-error-foreground: #0f172a;
95
- --keepui-success: #4ade80;
96
- --keepui-warning: #fbbf24;
97
-
98
- --keepui-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
99
- --keepui-shadow-md: 0 3px 6px rgba(0, 0, 0, 0.45);
100
- --keepui-shadow-lg: 0 6px 12px rgba(0, 0, 0, 0.5);
62
+ --ku-primary: #050505;
63
+ --ku-primary-hover: #2C2D31;
64
+ --ku-primary-text: #FFFFFF;
65
+ --ku-primary-border: #2C2D31;
66
+
67
+ --ku-secondary: #15161A;
68
+ --ku-secondary-hover: #2C2D31;
69
+ --ku-secondary-text: #FFFFFF;
70
+ --ku-secondary-border: #2C2D31;
71
+
72
+ --ku-gray-bg: #1F2937;
73
+ --ku-gray-border: #4B5563;
74
+ --ku-gray-text: #9CA3AF;
75
+
76
+ --ku-brand-bg: #2E1065;
77
+ --ku-brand-border: #7C3AED;
78
+ --ku-brand-text: #C4B5FD;
79
+
80
+ --ku-indigo-bg: #1E1B4B;
81
+ --ku-indigo-border: #4338CA;
82
+ --ku-indigo-text: #A5B4FC;
83
+
84
+ --ku-blue-bg: #0C1D3E;
85
+ --ku-blue-border: #2563EB;
86
+ --ku-blue-text: #93C5FD;
87
+
88
+ --ku-pink-bg: #3D0B2F;
89
+ --ku-pink-border: #DB2777;
90
+ --ku-pink-text: #F9A8D4;
91
+
92
+ --ku-orange-bg: #2C1204;
93
+ --ku-orange-border: #EA580C;
94
+ --ku-orange-text: #FDBA74;
95
+
96
+ --ku-yellow-bg: #2C1F02;
97
+ --ku-yellow-border: #CA8A04;
98
+ --ku-yellow-text: #FDE047;
99
+
100
+ --ku-green-bg: #052E16;
101
+ --ku-green-border: #16A34A;
102
+ --ku-green-text: #4ADE80;
103
+
104
+ --ku-purple-bg: #1C1040;
105
+ --ku-purple-border: #6D28D9;
106
+ --ku-purple-text: #C4B5FD;
107
+
108
+ --ku-teal-bg: #042A28;
109
+ --ku-teal-border: #0D9488;
110
+ --ku-teal-text: #2DD4BF;
111
+
112
+ --ku-red-bg: #2D0B0B;
113
+ --ku-red-border: #DC2626;
114
+ --ku-red-text: #FCA5A5;
115
+
116
+ /* =====================================================
117
+ SPACING
118
+ ===================================================== */
119
+
120
+ --spacing-xs: 4px;
121
+ --spacing-sm: 8px;
122
+ --spacing-md: 16px;
123
+ --spacing-lg: 24px;
124
+ --spacing-xl: 32px;
125
+ --spacing-2xl: 48px;
126
+ --spacing-3xl: 64px;
127
+ --spacing-4xl: 96px;
128
+
129
+ /* =====================================================
130
+ BORDER RADIUS
131
+ ===================================================== */
132
+
133
+ --radius-sm: 4px;
134
+ --radius-md: 8px;
135
+ --radius-lg: 12px;
136
+ --radius-xl: 16px;
137
+ --radius-2xl: 24px;
138
+ --radius-full: 9999px;
139
+
140
+ /* =====================================================
141
+ BOX SHADOW
142
+ ===================================================== */
143
+
144
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
145
+ --shadow: 1px 1px 12px 0 rgba(0, 0, 0, 0.4);
146
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
147
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
148
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
149
+ --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
150
+
151
+ /* =====================================================
152
+ Z-INDEX
153
+ ===================================================== */
154
+
155
+ --z-index-base: 0;
156
+ --z-index-dropdown: 1000;
157
+ --z-index-sticky: 1020;
158
+ --z-index-fixed: 1030;
159
+ --z-index-modal-backdrop: 1040;
160
+ --z-index-modal: 1050;
161
+ --z-index-popover: 1060;
162
+ --z-index-tooltip: 1070;
101
163
  }
102
164
 
103
165
  /* ===== Auto dark (OS preference) ===== */
104
166
  @media (prefers-color-scheme: dark) {
105
167
  :root:not([data-theme="light"]):not(.light) {
106
- --keepui-primary: #60a5fa;
107
- --keepui-primary-hover: #93c5fd;
108
- --keepui-primary-active: #3b82f6;
109
- --keepui-primary-foreground: #0f172a;
110
-
111
- --keepui-background: #0f172a;
112
- --keepui-surface: #1e293b;
113
- --keepui-surface-hover: #334155;
114
-
115
- --keepui-border: #334155;
116
- --keepui-border-strong: #475569;
117
-
118
- --keepui-text: #f1f5f9;
119
- --keepui-text-muted: #94a3b8;
120
- --keepui-text-disabled: #64748b;
121
-
122
- --keepui-error: #f87171;
123
- --keepui-error-foreground: #0f172a;
124
- --keepui-success: #4ade80;
125
- --keepui-warning: #fbbf24;
126
-
127
- --keepui-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
128
- --keepui-shadow-md: 0 3px 6px rgba(0, 0, 0, 0.45);
129
- --keepui-shadow-lg: 0 6px 12px rgba(0, 0, 0, 0.5);
168
+ /* ── Semantic color palette (dark) ── */
169
+ --ku-gray-bg: #1F2937;
170
+ --ku-gray-border: #4B5563;
171
+ --ku-gray-text: #9CA3AF;
172
+
173
+ --ku-brand-bg: #2E1065;
174
+ --ku-brand-border: #7C3AED;
175
+ --ku-brand-text: #C4B5FD;
176
+
177
+ --ku-indigo-bg: #1E1B4B;
178
+ --ku-indigo-border: #4338CA;
179
+ --ku-indigo-text: #A5B4FC;
180
+
181
+ --ku-blue-bg: #0C1D3E;
182
+ --ku-blue-border: #2563EB;
183
+ --ku-blue-text: #93C5FD;
184
+
185
+ --ku-pink-bg: #3D0B2F;
186
+ --ku-pink-border: #DB2777;
187
+ --ku-pink-text: #F9A8D4;
188
+
189
+ --ku-orange-bg: #2C1204;
190
+ --ku-orange-border: #EA580C;
191
+ --ku-orange-text: #FDBA74;
192
+
193
+ --ku-yellow-bg: #2C1F02;
194
+ --ku-yellow-border: #CA8A04;
195
+ --ku-yellow-text: #FDE047;
196
+
197
+ --ku-green-bg: #052E16;
198
+ --ku-green-border: #16A34A;
199
+ --ku-green-text: #4ADE80;
200
+
201
+ --ku-purple-bg: #1C1040;
202
+ --ku-purple-border: #6D28D9;
203
+ --ku-purple-text: #C4B5FD;
204
+
205
+ --ku-teal-bg: #042A28;
206
+ --ku-teal-border: #0D9488;
207
+ --ku-teal-text: #2DD4BF;
208
+
209
+ --ku-red-bg: #2D0B0B;
210
+ --ku-red-border: #DC2626;
211
+ --ku-red-text: #FCA5A5;
130
212
  }
131
213
  }
132
214