@cuencor/styles 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/_layout.scss ADDED
@@ -0,0 +1,538 @@
1
+ /* ── Grid system ─────────────────────────────────────────────────────────── */
2
+ // Grid de 12 columnas con breakpoints responsivos.
3
+ //
4
+ // Breakpoints:
5
+ // xs — < 480px (mobile pequeño — sin prefijo, siempre activo)
6
+ // sm — ≥ 480px (mobile)
7
+ // md — ≥ 768px (tablet)
8
+ // lg — ≥ 1024px (desktop)
9
+ // xl — ≥ 1280px (desktop ancho)
10
+ //
11
+ // Uso:
12
+ // <div class="fn-row">
13
+ // <!-- Full en mobile, mitad en tablet, tercio en desktop -->
14
+ // <div class="fn-col-12 fn-col-md-6 fn-col-lg-4">...</div>
15
+ // </div>
16
+ //
17
+ // Las clases sin prefijo (fn-col-N) son el default mobile-first.
18
+ // Cada breakpoint sobreescribe hacia arriba.
19
+
20
+ $fn-breakpoints: (
21
+ 'sm': 480px,
22
+ 'md': 768px,
23
+ 'lg': 1024px,
24
+ 'xl': 1280px,
25
+ );
26
+
27
+ .fn-row {
28
+ display: grid;
29
+ grid-template-columns: repeat(12, 1fr);
30
+ gap: 20px;
31
+ align-items: start;
32
+
33
+ // Variantes de gap
34
+ &--sm {
35
+ gap: 12px;
36
+ }
37
+ &--lg {
38
+ gap: 28px;
39
+ }
40
+ }
41
+
42
+ // Separador de sección dentro de un formulario
43
+ // Uso: <div class="fn-form-section">Información de la empresa</div>
44
+ .fn-form-section {
45
+ grid-column: span 12;
46
+ font-size: var(--fn-text-xs);
47
+ font-weight: 700;
48
+ color: var(--fn-gray-400);
49
+ text-transform: uppercase;
50
+ letter-spacing: 0.07em;
51
+ padding-bottom: 8px;
52
+ border-bottom: 1px solid var(--fn-gray-100);
53
+ margin-bottom: 4px;
54
+ }
55
+
56
+ // Base — mobile first (sin prefijo): siempre aplica
57
+ @for $i from 1 through 12 {
58
+ .fn-col-#{$i} {
59
+ grid-column: span #{$i};
60
+ }
61
+ }
62
+
63
+ // Breakpoints — sobreescriben el valor base hacia arriba
64
+ @each $bp, $width in $fn-breakpoints {
65
+ @media (min-width: #{$width}) {
66
+ @for $i from 1 through 12 {
67
+ .fn-col-#{$bp}-#{$i} {
68
+ grid-column: span #{$i};
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ /* ── Auth layout (login / register) ─────────────────────────────────────── */
75
+
76
+ .fn-auth {
77
+ min-height: 100vh;
78
+ background: linear-gradient(135deg, var(--fn-primary-50) 0%, var(--fn-primary-100) 100%);
79
+ display: flex;
80
+ align-items: flex-start;
81
+ justify-content: center;
82
+ padding: 24px 16px;
83
+ overflow-y: auto;
84
+ }
85
+
86
+ .fn-auth__card {
87
+ background: #fff;
88
+ border-radius: var(--fn-radius-xl);
89
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
90
+ width: 100%;
91
+ max-width: 480px;
92
+ padding: 24px 28px;
93
+ margin: auto 0;
94
+
95
+ &--lg {
96
+ max-width: 480px;
97
+ }
98
+ }
99
+
100
+ .fn-auth__logo {
101
+ display: flex;
102
+ flex-direction: column;
103
+ align-items: center;
104
+ text-align: center;
105
+ margin-bottom: 12px;
106
+ }
107
+
108
+ .fn-auth__logo-icon {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ width: 44px;
113
+ height: 44px;
114
+ background: var(--fn-primary-700);
115
+ border-radius: var(--fn-radius-lg);
116
+ margin-bottom: 8px;
117
+ color: #fff;
118
+ }
119
+
120
+ .fn-auth__logo-title {
121
+ font-size: var(--fn-text-xl);
122
+ font-weight: 700;
123
+ color: var(--fn-primary-700);
124
+ margin: 0 0 2px;
125
+ }
126
+
127
+ .fn-auth__logo-subtitle {
128
+ font-size: var(--fn-text-xs);
129
+ color: var(--fn-gray-500);
130
+ margin: 0;
131
+ }
132
+
133
+ .fn-auth__heading {
134
+ font-size: var(--fn-text-lg);
135
+ font-weight: 600;
136
+ color: var(--fn-gray-800);
137
+ text-align: center;
138
+ margin: 0 0 10px;
139
+ }
140
+
141
+ .fn-auth__section-title {
142
+ font-size: var(--fn-text-xs);
143
+ font-weight: 700;
144
+ color: var(--fn-gray-400);
145
+ text-transform: uppercase;
146
+ letter-spacing: 0.06em;
147
+ margin: 0 0 8px;
148
+ }
149
+
150
+ .fn-auth__form {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: 10px;
154
+
155
+ // Anula el margin-bottom del fn-form-group para no duplicar espaciado
156
+ .fn-form-group {
157
+ margin-bottom: 0;
158
+ }
159
+ }
160
+
161
+ .fn-auth__footer {
162
+ border-top: 1px solid var(--fn-gray-200);
163
+ margin-top: 14px;
164
+ padding-top: 12px;
165
+ text-align: center;
166
+ font-size: var(--fn-text-xs);
167
+ color: var(--fn-gray-400);
168
+ }
169
+
170
+ .fn-auth__link {
171
+ display: block;
172
+ text-align: center;
173
+ font-size: var(--fn-text-sm);
174
+ color: var(--fn-gray-500);
175
+ margin-top: 12px;
176
+
177
+ a {
178
+ color: var(--fn-primary-700);
179
+ font-weight: 500;
180
+ text-decoration: none;
181
+ &:hover {
182
+ text-decoration: underline;
183
+ }
184
+ }
185
+ }
186
+
187
+ .fn-auth__actions {
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: space-between;
191
+ gap: 12px;
192
+ margin-top: 14px;
193
+ padding-top: 12px;
194
+ border-top: 1px solid var(--fn-gray-200);
195
+
196
+ a {
197
+ font-size: var(--fn-text-sm);
198
+ color: var(--fn-primary-700);
199
+ text-decoration: none;
200
+ &:hover {
201
+ text-decoration: underline;
202
+ }
203
+ }
204
+ }
205
+
206
+ /* Spinner para auth (tenant selector loading) */
207
+ .fn-auth__spinner {
208
+ display: flex;
209
+ justify-content: center;
210
+ padding: 12px 0;
211
+ }
212
+
213
+ /* ── Accessibility utility ───────────────────────────────────────────────── */
214
+
215
+ .fn-sr-only {
216
+ position: absolute;
217
+ width: 1px;
218
+ height: 1px;
219
+ padding: 0;
220
+ margin: -1px;
221
+ overflow: hidden;
222
+ clip: rect(0, 0, 0, 0);
223
+ white-space: nowrap;
224
+ border: 0;
225
+ }
226
+
227
+ /* ── Page layout ────────────────────────────────────────────────────────── */
228
+
229
+ .fn-page {
230
+ padding: 15px;
231
+ max-width: 1280px;
232
+ width: 100%;
233
+ margin: 0 auto;
234
+ display: flex;
235
+ flex-direction: column;
236
+ gap: 24px;
237
+
238
+ // Full-width variant — removes max-width constraint
239
+ &--full {
240
+ max-width: none;
241
+ }
242
+ }
243
+
244
+ .fn-page__header {
245
+ display: flex;
246
+ align-items: flex-start;
247
+ justify-content: space-between;
248
+ gap: 16px;
249
+ flex-wrap: wrap;
250
+ }
251
+
252
+ .fn-page__title {
253
+ font-size: var(--fn-text-2xl);
254
+ font-weight: 700;
255
+ color: var(--fn-gray-900);
256
+ margin: 0;
257
+ }
258
+
259
+ .fn-page__subtitle {
260
+ font-size: var(--fn-text-sm);
261
+ color: var(--fn-gray-500);
262
+ margin: 4px 0 0;
263
+ }
264
+
265
+ /* ── Sidebar ── */
266
+ .fn-sidebar {
267
+ width: 260px;
268
+ min-height: 100vh;
269
+ background-color: var(--fn-primary-700);
270
+ color: #fff;
271
+ flex-shrink: 0;
272
+ display: flex;
273
+ flex-direction: column;
274
+ transition: transform var(--fn-transition-md);
275
+ overflow-y: auto;
276
+ }
277
+
278
+ /* ── Divider ── */
279
+ .fn-divider {
280
+ height: 1px;
281
+ background: var(--fn-gray-200);
282
+ border: none;
283
+ margin: 16px 0;
284
+
285
+ &--vertical {
286
+ width: 1px;
287
+ height: auto;
288
+ margin: 0 12px;
289
+ align-self: stretch;
290
+ }
291
+ }
292
+
293
+ /* ── Tabs ── */
294
+ .fn-tabs {
295
+ display: flex;
296
+ border-bottom: 1px solid var(--fn-gray-200);
297
+ overflow-x: auto;
298
+ gap: 0;
299
+ }
300
+
301
+ .fn-tab {
302
+ display: flex;
303
+ align-items: center;
304
+ gap: 6px;
305
+ padding: 10px 16px;
306
+ font-family: var(--fn-font-sans);
307
+ font-size: var(--fn-text-sm);
308
+ font-weight: 500;
309
+ color: var(--fn-gray-500);
310
+ border-bottom: 2px solid transparent;
311
+ border-top: none;
312
+ border-left: none;
313
+ border-right: none;
314
+ background: transparent;
315
+ cursor: pointer;
316
+ white-space: nowrap;
317
+ transition:
318
+ color var(--fn-transition),
319
+ border-color var(--fn-transition);
320
+
321
+ &:hover {
322
+ color: var(--fn-gray-700);
323
+ }
324
+ &--active {
325
+ color: var(--fn-primary-700);
326
+ border-bottom-color: var(--fn-primary-700);
327
+ font-weight: 700;
328
+ }
329
+ &:disabled {
330
+ opacity: 0.4;
331
+ cursor: not-allowed;
332
+ }
333
+ }
334
+
335
+ /* ── Pagination ── */
336
+ .fn-pagination {
337
+ display: flex;
338
+ align-items: center;
339
+ gap: 4px;
340
+ flex-wrap: wrap;
341
+
342
+ &__btn {
343
+ display: inline-flex;
344
+ align-items: center;
345
+ justify-content: center;
346
+ min-width: 34px;
347
+ height: 34px;
348
+ padding: 0 6px;
349
+ font-family: var(--fn-font-sans);
350
+ font-size: var(--fn-text-sm);
351
+ border: 1px solid var(--fn-gray-200);
352
+ border-radius: var(--fn-radius);
353
+ background: #fff;
354
+ color: var(--fn-gray-600);
355
+ cursor: pointer;
356
+ transition:
357
+ background var(--fn-transition),
358
+ border-color var(--fn-transition),
359
+ color var(--fn-transition);
360
+
361
+ &:hover {
362
+ background: var(--fn-gray-50);
363
+ border-color: var(--fn-gray-300);
364
+ }
365
+ &--active {
366
+ background: var(--fn-primary-700);
367
+ border-color: var(--fn-primary-700);
368
+ color: #fff;
369
+ }
370
+ &:disabled {
371
+ opacity: 0.4;
372
+ cursor: not-allowed;
373
+ }
374
+ }
375
+
376
+ &__info {
377
+ font-size: var(--fn-text-sm);
378
+ color: var(--fn-gray-500);
379
+ padding: 0 8px;
380
+ }
381
+ }
382
+
383
+ /* ── Empty state ── */
384
+ .fn-empty-state {
385
+ display: flex;
386
+ flex-direction: column;
387
+ align-items: center;
388
+ text-align: center;
389
+ padding: 48px 24px;
390
+
391
+ &__icon {
392
+ font-size: 48px;
393
+ margin-bottom: 12px;
394
+ line-height: 1;
395
+ }
396
+ &__title {
397
+ font-size: var(--fn-text-lg);
398
+ font-weight: 700;
399
+ color: var(--fn-gray-700);
400
+ margin: 0 0 6px;
401
+ }
402
+ &__text {
403
+ font-size: var(--fn-text-sm);
404
+ color: var(--fn-gray-500);
405
+ margin: 0 0 16px;
406
+ max-width: 420px;
407
+ }
408
+ &__description {
409
+ font-size: var(--fn-text-sm);
410
+ color: var(--fn-gray-500);
411
+ margin: 0 0 16px;
412
+ }
413
+ &__actions {
414
+ display: flex;
415
+ gap: 10px;
416
+ flex-wrap: wrap;
417
+ justify-content: center;
418
+ margin-top: 4px;
419
+ }
420
+ }
421
+
422
+ /* ── Filter bar ─────────────────────────────────────────────────────────── */
423
+
424
+ /**
425
+ * fn-filter-bar: barra de filtros unificada para listados.
426
+ *
427
+ * Estructura:
428
+ * <div class="fn-filter-bar">
429
+ * <div class="fn-filter-bar__search"> <!-- search input (flex: 1) -->
430
+ * <div class="fn-filter-bar__fields"> <!-- grupo de selects/dates compactos -->
431
+ * <div class="fn-filter-bar__field"> <!-- cada select/input con label -->
432
+ * <label class="fn-filter-bar__label">
433
+ * <select class="fn-select fn-select--sm">
434
+ * <div class="fn-filter-bar__actions"> <!-- botones Filtrar / Limpiar / contador -->
435
+ * <span class="fn-filter-bar__count"> <!-- "N resultados" -->
436
+ * <button class="fn-btn fn-btn--ghost fn-btn--sm"> <!-- Limpiar -->
437
+ * <button class="fn-btn fn-btn--primary fn-btn--sm"> <!-- Filtrar (si aplica) -->
438
+ */
439
+
440
+ .fn-filter-bar {
441
+ display: flex;
442
+ align-items: flex-end;
443
+ gap: 12px;
444
+ padding: 14px 16px;
445
+ background: #fff;
446
+ border: 1px solid var(--fn-gray-200);
447
+ border-radius: var(--fn-radius-lg);
448
+ flex-wrap: wrap;
449
+
450
+ &__search {
451
+ flex: 1;
452
+ min-width: 200px;
453
+ }
454
+
455
+ &__fields {
456
+ display: flex;
457
+ align-items: flex-end;
458
+ gap: 10px;
459
+ flex-wrap: wrap;
460
+ }
461
+
462
+ &__field {
463
+ display: flex;
464
+ flex-direction: column;
465
+ gap: 4px;
466
+ min-width: 130px;
467
+ }
468
+
469
+ &__label {
470
+ font-size: 11px;
471
+ font-weight: 600;
472
+ color: var(--fn-gray-500);
473
+ text-transform: uppercase;
474
+ letter-spacing: 0.04em;
475
+ white-space: nowrap;
476
+ }
477
+
478
+ &__actions {
479
+ display: flex;
480
+ align-items: center;
481
+ gap: 8px;
482
+ margin-left: auto;
483
+ flex-shrink: 0;
484
+ }
485
+
486
+ &__count {
487
+ font-size: var(--fn-text-sm);
488
+ color: var(--fn-gray-500);
489
+ white-space: nowrap;
490
+ }
491
+
492
+ &__active-badge {
493
+ display: inline-flex;
494
+ align-items: center;
495
+ justify-content: center;
496
+ min-width: 18px;
497
+ height: 18px;
498
+ padding: 0 5px;
499
+ background: var(--fn-primary-700);
500
+ color: #fff;
501
+ font-size: 10px;
502
+ font-weight: 700;
503
+ border-radius: var(--fn-radius-full);
504
+ }
505
+ }
506
+
507
+ /* ── Avatar ── */
508
+ .fn-avatar {
509
+ display: inline-flex;
510
+ align-items: center;
511
+ justify-content: center;
512
+ border-radius: 50%;
513
+ background: var(--fn-primary-100);
514
+ color: var(--fn-primary-700);
515
+ font-weight: 700;
516
+ flex-shrink: 0;
517
+
518
+ &--sm {
519
+ width: 28px;
520
+ height: 28px;
521
+ font-size: 11px;
522
+ }
523
+ &--md {
524
+ width: 36px;
525
+ height: 36px;
526
+ font-size: 14px;
527
+ }
528
+ &--lg {
529
+ width: 48px;
530
+ height: 48px;
531
+ font-size: 18px;
532
+ }
533
+ &--xl {
534
+ width: 64px;
535
+ height: 64px;
536
+ font-size: 24px;
537
+ }
538
+ }
package/_modals.scss ADDED
@@ -0,0 +1,120 @@
1
+ /* ── Modals ─────────────────────────────────────────────────────────────── */
2
+
3
+ .fn-modal-backdrop {
4
+ position: fixed;
5
+ inset: 0;
6
+ background: rgba(0, 0, 0, 0.45);
7
+ z-index: 400;
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ padding: 16px;
12
+ animation: fn-fade-in 0.15s ease;
13
+ }
14
+
15
+ .fn-modal {
16
+ background: #fff;
17
+ border-radius: var(--fn-radius-xl);
18
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
19
+ width: 100%;
20
+ max-width: 480px;
21
+ max-height: 90vh;
22
+ overflow-y: auto;
23
+ animation: fn-modal-in 0.2s ease;
24
+
25
+ &--sm { max-width: 360px; }
26
+ &--lg { max-width: 640px; }
27
+ &--xl { max-width: 800px; }
28
+ &--full { max-width: calc(100vw - 32px); max-height: calc(100vh - 32px); }
29
+ }
30
+
31
+ .fn-modal__header {
32
+ padding: 20px 24px 0;
33
+ display: flex;
34
+ align-items: flex-start;
35
+ justify-content: space-between;
36
+ gap: 12px;
37
+ }
38
+
39
+ .fn-modal__title {
40
+ font-size: var(--fn-text-xl);
41
+ font-weight: 700;
42
+ color: var(--fn-gray-900);
43
+ margin: 0;
44
+ line-height: 1.3;
45
+ }
46
+
47
+ .fn-modal__subtitle {
48
+ font-size: var(--fn-text-sm);
49
+ color: var(--fn-gray-500);
50
+ margin: 4px 0 0;
51
+ }
52
+
53
+ .fn-modal__close {
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ width: 32px;
58
+ height: 32px;
59
+ background: transparent;
60
+ border: none;
61
+ border-radius: var(--fn-radius);
62
+ color: var(--fn-gray-400);
63
+ font-size: 20px;
64
+ cursor: pointer;
65
+ flex-shrink: 0;
66
+ transition: background var(--fn-transition), color var(--fn-transition);
67
+
68
+ &:hover { background: var(--fn-gray-100); color: var(--fn-gray-700); }
69
+ }
70
+
71
+ .fn-modal__body {
72
+ padding: 16px 24px;
73
+ color: var(--fn-gray-600);
74
+ font-size: var(--fn-text-sm);
75
+ line-height: 1.6;
76
+ }
77
+
78
+ .fn-modal__footer {
79
+ padding: 0 24px 20px;
80
+ display: flex;
81
+ justify-content: flex-end;
82
+ gap: 8px;
83
+ flex-wrap: wrap;
84
+ }
85
+
86
+ /* ── Idle timeout modal ── */
87
+ .fn-idle-backdrop {
88
+ position: fixed;
89
+ inset: 0;
90
+ z-index: 40;
91
+ background: rgba(0, 0, 0, 0.5);
92
+ }
93
+
94
+ .fn-idle-modal {
95
+ position: fixed;
96
+ z-index: 50;
97
+ background: #fff;
98
+ border-radius: var(--fn-radius-xl);
99
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
100
+ padding: 2rem;
101
+ text-align: center;
102
+ top: 50%;
103
+ left: 50%;
104
+ transform: translate(-50%, -50%);
105
+ width: min(420px, calc(100vw - 2rem));
106
+
107
+ &__icon { font-size: 3rem; line-height: 1; margin-bottom: 1rem; }
108
+ &__title { font-size: var(--fn-text-xl); font-weight: 700; color: var(--fn-gray-900); margin-bottom: 0.5rem; }
109
+ &__desc { color: var(--fn-gray-600); font-size: var(--fn-text-sm); margin-bottom: 1.5rem; line-height: 1.625; }
110
+ &__timer { font-weight: 700; font-size: var(--fn-text-base); color: var(--fn-danger-600); }
111
+ &__actions {
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: 0.75rem;
115
+ @media (min-width: 480px) {
116
+ flex-direction: row;
117
+ justify-content: center;
118
+ }
119
+ }
120
+ }