secure-ui-components 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +368 -38
  2. package/dist/components/secure-card/secure-card.css +427 -0
  3. package/dist/components/secure-card/secure-card.d.ts +73 -0
  4. package/dist/components/secure-card/secure-card.d.ts.map +1 -0
  5. package/dist/components/secure-card/secure-card.js +787 -0
  6. package/dist/components/secure-card/secure-card.js.map +1 -0
  7. package/dist/components/secure-datetime/secure-datetime.d.ts.map +1 -1
  8. package/dist/components/secure-datetime/secure-datetime.js +6 -19
  9. package/dist/components/secure-datetime/secure-datetime.js.map +1 -1
  10. package/dist/components/secure-file-upload/secure-file-upload.d.ts.map +1 -1
  11. package/dist/components/secure-file-upload/secure-file-upload.js +0 -16
  12. package/dist/components/secure-file-upload/secure-file-upload.js.map +1 -1
  13. package/dist/components/secure-form/secure-form.d.ts.map +1 -1
  14. package/dist/components/secure-form/secure-form.js +126 -6
  15. package/dist/components/secure-form/secure-form.js.map +1 -1
  16. package/dist/components/secure-input/secure-input.d.ts.map +1 -1
  17. package/dist/components/secure-input/secure-input.js +6 -21
  18. package/dist/components/secure-input/secure-input.js.map +1 -1
  19. package/dist/components/secure-select/secure-select.d.ts.map +1 -1
  20. package/dist/components/secure-select/secure-select.js +6 -19
  21. package/dist/components/secure-select/secure-select.js.map +1 -1
  22. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.d.ts +63 -0
  23. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.d.ts.map +1 -0
  24. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js +198 -0
  25. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js.map +1 -0
  26. package/dist/components/secure-textarea/secure-textarea.d.ts.map +1 -1
  27. package/dist/components/secure-textarea/secure-textarea.js +6 -19
  28. package/dist/components/secure-textarea/secure-textarea.js.map +1 -1
  29. package/dist/core/base-component.d.ts +23 -1
  30. package/dist/core/base-component.d.ts.map +1 -1
  31. package/dist/core/base-component.js +111 -0
  32. package/dist/core/base-component.js.map +1 -1
  33. package/dist/core/security-config.d.ts.map +1 -1
  34. package/dist/core/security-config.js +5 -21
  35. package/dist/core/security-config.js.map +1 -1
  36. package/dist/core/types.d.ts +129 -8
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/index.d.ts +3 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/package.json +4 -2
  43. package/package.json +13 -1
@@ -0,0 +1,427 @@
1
+ /* ── Design tokens (card-specific) ──────────────────────────────────────── */
2
+ :host {
3
+ --secure-card-width: 340px;
4
+ --secure-card-radius: 16px;
5
+ --secure-card-font: var(--secure-ui-font-family-mono, 'Courier New', monospace);
6
+ --secure-card-transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
7
+
8
+ /* Default gradient — overridden per card type */
9
+ --secure-card-gradient: linear-gradient(135deg, #1e2a4a 0%, #2d4a8a 100%);
10
+
11
+ display: block;
12
+ }
13
+
14
+ /* ── Container ───────────────────────────────────────────────────────────── */
15
+ .card-container {
16
+ width: 100%;
17
+ max-width: 480px;
18
+ font-family: var(--secure-ui-font-family-sans, system-ui, sans-serif);
19
+ }
20
+
21
+ /* ── Card scene — 3D perspective wrapper ─────────────────────────────────── */
22
+ .card-scene {
23
+ width: var(--secure-card-width);
24
+ max-width: 100%;
25
+ aspect-ratio: 85.6 / 54;
26
+ perspective: 1000px;
27
+ margin-inline: auto;
28
+ margin-block-end: var(--secure-ui-space-6, 1.5rem);
29
+ user-select: none;
30
+ }
31
+
32
+ .card {
33
+ width: 100%;
34
+ height: 100%;
35
+ position: relative;
36
+ transform-style: preserve-3d;
37
+ transition: var(--secure-card-transition);
38
+ border-radius: var(--secure-card-radius);
39
+ /* Subtle lift shadow */
40
+ filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.35));
41
+ }
42
+
43
+ .card.is-flipped {
44
+ transform: rotateY(180deg);
45
+ }
46
+
47
+ /* ── Card faces ──────────────────────────────────────────────────────────── */
48
+ .card-face {
49
+ position: absolute;
50
+ inset: 0;
51
+ border-radius: var(--secure-card-radius);
52
+ backface-visibility: hidden;
53
+ -webkit-backface-visibility: hidden;
54
+ background: var(--secure-card-gradient);
55
+ color: #fff;
56
+ overflow: hidden;
57
+ }
58
+
59
+ /* Subtle gloss overlay */
60
+ .card-face::before {
61
+ content: '';
62
+ position: absolute;
63
+ inset: 0;
64
+ background: linear-gradient(
65
+ 135deg,
66
+ rgba(255, 255, 255, 0.15) 0%,
67
+ rgba(255, 255, 255, 0) 60%
68
+ );
69
+ border-radius: inherit;
70
+ pointer-events: none;
71
+ }
72
+
73
+ .card-back {
74
+ transform: rotateY(180deg);
75
+ }
76
+
77
+ /* ── Card type gradients ─────────────────────────────────────────────────── */
78
+ .card--visa {
79
+ --secure-card-gradient: linear-gradient(135deg, #1a1f71 0%, #2563eb 100%);
80
+ }
81
+
82
+ .card--mastercard {
83
+ --secure-card-gradient: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #e63946 100%);
84
+ }
85
+
86
+ .card--amex {
87
+ --secure-card-gradient: linear-gradient(135deg, #007b5e 0%, #00b894 100%);
88
+ }
89
+
90
+ .card--discover {
91
+ --secure-card-gradient: linear-gradient(135deg, #b34700 0%, #e65c00 50%, #f9a825 100%);
92
+ }
93
+
94
+ .card--diners {
95
+ --secure-card-gradient: linear-gradient(135deg, #005c97 0%, #363795 100%);
96
+ }
97
+
98
+ .card--jcb {
99
+ --secure-card-gradient: linear-gradient(135deg, #0e4c96 0%, #e63946 100%);
100
+ }
101
+
102
+ /* ── Card front layout ───────────────────────────────────────────────────── */
103
+ .card-front {
104
+ display: flex;
105
+ flex-direction: column;
106
+ justify-content: space-between;
107
+ padding: 20px 24px 18px;
108
+ }
109
+
110
+ .card-top-row {
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: space-between;
114
+ }
115
+
116
+ /* EMV chip */
117
+ .card-chip {
118
+ width: 42px;
119
+ height: 32px;
120
+ background: linear-gradient(135deg, #c8a84b 0%, #f0d060 40%, #c8a84b 100%);
121
+ border-radius: 5px;
122
+ position: relative;
123
+ flex-shrink: 0;
124
+ }
125
+
126
+ .card-chip::before {
127
+ content: '';
128
+ position: absolute;
129
+ inset: 9px 0;
130
+ border-top: 1.5px solid rgba(0, 0, 0, 0.25);
131
+ border-bottom: 1.5px solid rgba(0, 0, 0, 0.25);
132
+ }
133
+
134
+ .card-chip::after {
135
+ content: '';
136
+ position: absolute;
137
+ inset: 0 13px;
138
+ border-left: 1.5px solid rgba(0, 0, 0, 0.2);
139
+ border-right: 1.5px solid rgba(0, 0, 0, 0.2);
140
+ border-radius: 2px;
141
+ }
142
+
143
+ /* Card brand label (top-right) */
144
+ .card-type-label {
145
+ font-size: 1rem;
146
+ font-weight: 700;
147
+ letter-spacing: 0.06em;
148
+ font-style: italic;
149
+ opacity: 0.95;
150
+ min-width: 52px;
151
+ text-align: end;
152
+ text-transform: uppercase;
153
+ }
154
+
155
+ /* Mastercard two-circles via pseudo-elements */
156
+ .card--mastercard .card-type-label {
157
+ position: relative;
158
+ width: 52px;
159
+ height: 32px;
160
+ font-size: 0; /* hide text, show circles only */
161
+ }
162
+
163
+ .card--mastercard .card-type-label::before,
164
+ .card--mastercard .card-type-label::after {
165
+ content: '';
166
+ position: absolute;
167
+ top: 50%;
168
+ transform: translateY(-50%);
169
+ width: 28px;
170
+ height: 28px;
171
+ border-radius: 50%;
172
+ }
173
+
174
+ .card--mastercard .card-type-label::before {
175
+ background: #eb001b;
176
+ inset-inline-start: 0;
177
+ }
178
+
179
+ .card--mastercard .card-type-label::after {
180
+ background: #f79e1b;
181
+ inset-inline-start: 16px;
182
+ mix-blend-mode: multiply;
183
+ }
184
+
185
+ /* Card number */
186
+ .card-number-display {
187
+ font-family: var(--secure-card-font);
188
+ font-size: clamp(0.85rem, 2.5vw, 1.1rem);
189
+ letter-spacing: 0.18em;
190
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
191
+ white-space: nowrap;
192
+ overflow: hidden;
193
+ text-overflow: ellipsis;
194
+ }
195
+
196
+ /* Bottom row */
197
+ .card-bottom-row {
198
+ display: flex;
199
+ justify-content: space-between;
200
+ align-items: flex-end;
201
+ gap: var(--secure-ui-space-3, 0.75rem);
202
+ }
203
+
204
+ .card-field-label {
205
+ font-size: 0.6rem;
206
+ text-transform: uppercase;
207
+ letter-spacing: 0.12em;
208
+ opacity: 0.7;
209
+ margin-block-end: 2px;
210
+ }
211
+
212
+ .card-name-display {
213
+ font-size: clamp(0.7rem, 2vw, 0.85rem);
214
+ letter-spacing: 0.06em;
215
+ font-weight: 500;
216
+ text-transform: uppercase;
217
+ white-space: nowrap;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
220
+ max-width: 160px;
221
+ }
222
+
223
+ .card-expiry-display {
224
+ font-family: var(--secure-card-font);
225
+ font-size: 0.85rem;
226
+ letter-spacing: 0.1em;
227
+ white-space: nowrap;
228
+ }
229
+
230
+ /* ── Card back layout ────────────────────────────────────────────────────── */
231
+ .card-back {
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: var(--secure-ui-space-3, 0.75rem);
235
+ }
236
+
237
+ /* Magnetic strip */
238
+ .card-strip {
239
+ width: 100%;
240
+ height: 40px;
241
+ background: #1a1a1a;
242
+ margin-block-start: 28px;
243
+ flex-shrink: 0;
244
+ }
245
+
246
+ /* CVC area */
247
+ .card-cvc-area {
248
+ display: flex;
249
+ flex-direction: column;
250
+ align-items: flex-end;
251
+ padding-inline-end: 24px;
252
+ gap: 4px;
253
+ }
254
+
255
+ .card-cvc-label {
256
+ font-size: 0.6rem;
257
+ text-transform: uppercase;
258
+ letter-spacing: 0.1em;
259
+ opacity: 0.7;
260
+ }
261
+
262
+ .card-cvc-display {
263
+ background: #fff;
264
+ color: #1a1a1a;
265
+ font-family: var(--secure-card-font);
266
+ font-size: 0.85rem;
267
+ letter-spacing: 0.2em;
268
+ padding: 4px 10px;
269
+ border-radius: 3px;
270
+ min-width: 52px;
271
+ text-align: center;
272
+ }
273
+
274
+ /* ── Input fields area ───────────────────────────────────────────────────── */
275
+ .card-fields {
276
+ display: flex;
277
+ flex-direction: column;
278
+ gap: var(--secure-ui-space-4, 1rem);
279
+ }
280
+
281
+ .card-legend {
282
+ font-size: var(--secure-ui-font-size-sm, 0.875rem);
283
+ font-weight: var(--secure-ui-font-weight-semibold, 600);
284
+ color: var(--secure-ui-color-neutral-700, #374151);
285
+ margin-block-end: var(--secure-ui-space-1, 0.25rem);
286
+ }
287
+
288
+ /* Expiry + CVC row */
289
+ .field-row {
290
+ display: grid;
291
+ grid-template-columns: 1fr 1fr;
292
+ gap: var(--secure-ui-space-4, 1rem);
293
+ }
294
+
295
+ .field-group {
296
+ display: flex;
297
+ flex-direction: column;
298
+ gap: var(--secure-ui-space-1, 0.25rem);
299
+ position: relative;
300
+ }
301
+
302
+ .field-label {
303
+ font-size: var(--secure-ui-font-size-sm, 0.875rem);
304
+ font-weight: var(--secure-ui-font-weight-medium, 500);
305
+ color: var(--secure-ui-color-neutral-700, #374151);
306
+ line-height: var(--secure-ui-line-height-tight, 1.25);
307
+ }
308
+
309
+ .input-wrapper {
310
+ position: relative;
311
+ }
312
+
313
+ /* ── Card inputs ─────────────────────────────────────────────────────────── */
314
+ .card-input {
315
+ width: 100%;
316
+ box-sizing: border-box;
317
+ padding: var(--secure-ui-input-padding-y, 0.625rem) var(--secure-ui-input-padding-x, 0.75rem);
318
+ font-family: var(--secure-ui-font-family-sans, system-ui, sans-serif);
319
+ font-size: var(--secure-ui-font-size-base, 1rem);
320
+ line-height: var(--secure-ui-line-height-normal, 1.5);
321
+ color: var(--secure-ui-input-color, #111827);
322
+ background: var(--secure-ui-input-bg, #fff);
323
+ border: var(--secure-ui-border-width, 1px) solid var(--secure-ui-input-border-color, #d1d5db);
324
+ border-radius: var(--secure-ui-input-border-radius, 6px);
325
+ outline: none;
326
+ transition:
327
+ border-color var(--secure-ui-transition-fast, 150ms) ease,
328
+ box-shadow var(--secure-ui-transition-fast, 150ms) ease;
329
+ appearance: none;
330
+ -webkit-appearance: none;
331
+ }
332
+
333
+ .card-input:focus {
334
+ border-color: var(--secure-ui-color-primary-500, #6366f1);
335
+ box-shadow: 0 0 0 3px var(--secure-ui-color-primary-100, rgba(99, 102, 241, 0.2));
336
+ }
337
+
338
+ .card-input[aria-invalid='true'] {
339
+ border-color: var(--secure-ui-color-error-500, #ef4444) !important;
340
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15);
341
+ }
342
+
343
+ .card-input:disabled {
344
+ background: var(--secure-ui-input-disabled-bg, #f3f4f6);
345
+ color: var(--secure-ui-color-neutral-400, #9ca3af);
346
+ cursor: not-allowed;
347
+ }
348
+
349
+ /* Monospace for card number and expiry — aids digit alignment */
350
+ .card-number-input {
351
+ font-family: var(--secure-card-font);
352
+ letter-spacing: 0.06em;
353
+ }
354
+
355
+ /* Password reveal button removed for CVC — keeps dots */
356
+ .cvc-input::-ms-reveal,
357
+ .cvc-input::-ms-clear {
358
+ display: none;
359
+ }
360
+
361
+ /* ── Error messages ──────────────────────────────────────────────────────── */
362
+ .error-container {
363
+ font-size: var(--secure-ui-font-size-xs, 0.75rem);
364
+ color: var(--secure-ui-color-error-600, #dc2626);
365
+ line-height: var(--secure-ui-line-height-tight, 1.25);
366
+ min-height: 0;
367
+ max-height: 3rem;
368
+ overflow: hidden;
369
+ transition:
370
+ max-height var(--secure-ui-transition-normal, 250ms) ease,
371
+ opacity var(--secure-ui-transition-fast, 150ms) ease;
372
+ opacity: 1;
373
+ }
374
+
375
+ .error-container.hidden {
376
+ max-height: 0;
377
+ opacity: 0;
378
+ }
379
+
380
+ /* ── Dark mode ───────────────────────────────────────────────────────────── */
381
+ @media (prefers-color-scheme: dark) {
382
+ .card-legend {
383
+ color: var(--secure-ui-color-neutral-200, #e5e7eb);
384
+ }
385
+
386
+ .field-label {
387
+ color: var(--secure-ui-color-neutral-200, #e5e7eb);
388
+ }
389
+
390
+ .card-input {
391
+ color: var(--secure-ui-input-color, #f9fafb);
392
+ background: var(--secure-ui-input-bg, #1f2937);
393
+ border-color: var(--secure-ui-input-border-color, #374151);
394
+ }
395
+
396
+ .card-input:disabled {
397
+ background: #111827;
398
+ color: #6b7280;
399
+ }
400
+
401
+ .card-cvc-display {
402
+ background: #f9fafb;
403
+ color: #111827;
404
+ }
405
+ }
406
+
407
+ /* ── Reduced motion ──────────────────────────────────────────────────────── */
408
+ @media (prefers-reduced-motion: reduce) {
409
+ .card {
410
+ transition: none;
411
+ }
412
+
413
+ .card-input {
414
+ transition: none;
415
+ }
416
+
417
+ .error-container {
418
+ transition: none;
419
+ }
420
+ }
421
+
422
+ /* ── Responsive ──────────────────────────────────────────────────────────── */
423
+ @media (max-width: 400px) {
424
+ .field-row {
425
+ grid-template-columns: 1fr;
426
+ }
427
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @fileoverview SecureCard — composite credit card input Web Component
3
+ *
4
+ * Renders card number, expiry date, CVC, and optional cardholder name
5
+ * fields inside a closed Shadow DOM with a live 3D card preview.
6
+ *
7
+ * Security model:
8
+ * - Full PAN is never included in events, audit logs, or hidden inputs
9
+ * - CVC is never in events, hidden inputs, or audit logs
10
+ * - Card number masked to last-4 on blur
11
+ * - Security tier is locked to CRITICAL (immutable, fail-secure default)
12
+ * - Luhn validation on card number
13
+ * - Rate limiting and audit logging via SecureBaseComponent
14
+ * - All sensitive fields cleared on disconnectedCallback
15
+ *
16
+ * PCI note: These fields provide a secure UI layer only. Raw card data
17
+ * must be passed to a PCI-compliant payment processor's SDK — never to
18
+ * your own server. Use getCardData() exclusively for SDK tokenisation.
19
+ *
20
+ * @module secure-card
21
+ * @license MIT
22
+ */
23
+ import { SecureBaseComponent } from '../../core/base-component.js';
24
+ import type { CardType } from '../../core/types.js';
25
+ export type { CardType };
26
+ export declare class SecureCard extends SecureBaseComponent {
27
+ #private;
28
+ constructor();
29
+ static get observedAttributes(): string[];
30
+ protected render(): DocumentFragment | HTMLElement | null;
31
+ protected handleAttributeChange(name: string, _oldValue: string | null, newValue: string | null): void;
32
+ disconnectedCallback(): void;
33
+ /**
34
+ * True when all visible, required fields pass validation.
35
+ */
36
+ get valid(): boolean;
37
+ /**
38
+ * Card type detected from the entered number prefix.
39
+ */
40
+ get cardType(): CardType;
41
+ /**
42
+ * Last 4 digits of the entered card number. Safe to display and log.
43
+ */
44
+ get last4(): string;
45
+ /**
46
+ * The name attribute value.
47
+ */
48
+ get name(): string;
49
+ /**
50
+ * Returns raw card data for immediate handoff to a payment SDK tokeniser.
51
+ *
52
+ * SECURITY: Pass this data only to a PCI-compliant processor's client SDK
53
+ * (e.g. Stripe.js createToken, Braintree tokenizeCard). Never send raw card
54
+ * numbers or CVCs to your own server.
55
+ *
56
+ * Returns null if the form is not yet valid.
57
+ */
58
+ getCardData(): {
59
+ number: string;
60
+ expiry: string;
61
+ cvc: string;
62
+ name: string;
63
+ } | null;
64
+ /**
65
+ * Clears all fields and resets component state.
66
+ */
67
+ reset(): void;
68
+ /**
69
+ * Focuses the card number input.
70
+ */
71
+ focus(): void;
72
+ }
73
+ //# sourceMappingURL=secure-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secure-card.d.ts","sourceRoot":"","sources":["../../../src/components/secure-card/secure-card.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,YAAY,EAAE,QAAQ,EAAE,CAAC;AA+HzB,qBAAa,UAAW,SAAQ,mBAAmB;;;IAwCjD,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAIkB,MAAM,IAAI,gBAAgB,GAAG,WAAW,GAAG,IAAI;cAwQ/C,qBAAqB,CACtC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI;IA8SE,oBAAoB,IAAI,IAAI;IAoBrC;;OAEG;IACH,IAAI,KAAK,IAAI,OAAO,CAqBnB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,CAElB;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;;;;OAQG;IACH,WAAW,IAAI;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI;IAUR;;OAEG;IACH,KAAK,IAAI,IAAI;IAgCb;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}