@higinio/design-mcp 0.1.0 → 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.
- package/LICENSE +4 -4
- package/README.md +11 -8
- package/assets/bruma/design-doc.md +1083 -0
- package/assets/bruma/framework.css +1374 -0
- package/assets/bruma/styleguide.html +4687 -0
- package/assets/bruma/tailwind.css +121 -0
- package/assets/bruma/tokens.css +147 -0
- package/assets/bruma/tokens.json +995 -0
- package/assets/celeste/design-doc.md +183 -17
- package/assets/celeste/framework.css +440 -3
- package/assets/celeste/styleguide.html +1534 -64
- package/assets/celeste/tailwind.css +4 -0
- package/assets/nocturna/design-doc.md +237 -47
- package/assets/nocturna/framework.css +429 -15
- package/assets/nocturna/styleguide.html +1521 -137
- package/assets/nocturna/tailwind.css +8 -2
- package/assets/nocturna/tokens.css +22 -11
- package/assets/nocturna/tokens.json +26 -1
- package/assets/salvia/design-doc.md +1067 -0
- package/assets/salvia/framework.css +1370 -0
- package/assets/salvia/styleguide.html +4670 -0
- package/assets/salvia/tailwind.css +121 -0
- package/assets/salvia/tokens.css +143 -0
- package/assets/salvia/tokens.json +1023 -0
- package/dist/cli.js +7 -5
- package/dist/doc.js +18 -4
- package/dist/preflight.js +2 -1
- package/dist/server.js +7 -7
- package/dist/slop.js +3 -3
- package/dist/systems.js +27 -1
- package/dist/validate.js +13 -5
- package/package.json +3 -3
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
/* -- Primitivos: acento -- */
|
|
27
27
|
--accent-100: #d9f0fb; --accent-200: #a8ddf6; --accent-300: #7dccf1;
|
|
28
28
|
--accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb; --accent-700: #186a94;
|
|
29
|
+
--accent-800: #12506f;
|
|
29
30
|
|
|
30
31
|
/* -- Primitivos: índigo -- */
|
|
31
32
|
--indigo-300: #7a83c4; --indigo-400: #565fa3; --indigo-500: #353d7c;
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
/* -- Primitivos: escala nocturna -- */
|
|
35
36
|
--night-950: #010203; --night-900: #0b0d12; --night-800: #14171f;
|
|
36
37
|
--night-700: #1e222d; --night-600: #2a2f3d; --night-500: #3d4354;
|
|
38
|
+
--night-450: #5f6575; /* texto atenuado en light */
|
|
37
39
|
--night-400: #6b7183; --night-300: #9aa0b0; --night-200: #c5c9d4;
|
|
38
40
|
--night-100: #e8eaef; --night-50: #f6f7f9;
|
|
39
41
|
|
|
@@ -49,7 +51,10 @@
|
|
|
49
51
|
--surface-2: var(--night-800);
|
|
50
52
|
--surface-3: var(--night-700);
|
|
51
53
|
--border: var(--night-500);
|
|
52
|
-
|
|
54
|
+
/* night-400, no night-600: en dark, oscurecer un borde lo DEBILITA (principio 2:
|
|
55
|
+
la elevación se comunica aclarando). n600 daba 1.46:1 vs los 1.97:1 del borde
|
|
56
|
+
normal — el énfasis se veía más flojo. n400 = 3.99:1 sobre surface-1. */
|
|
57
|
+
--border-strong: var(--night-400);
|
|
53
58
|
--text-primary: var(--night-100);
|
|
54
59
|
--text-secondary: var(--night-200);
|
|
55
60
|
--text-muted: var(--night-300);
|
|
@@ -120,7 +125,7 @@
|
|
|
120
125
|
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
|
|
121
126
|
--radius-xl: 24px; --radius-full: 9999px;
|
|
122
127
|
--border-default: 1px solid var(--night-500);
|
|
123
|
-
--border-emphasis: 1px solid var(--night-
|
|
128
|
+
--border-emphasis: 1px solid var(--night-400); /* ver --border-strong */
|
|
124
129
|
--focus-ring: 2px solid var(--accent-400);
|
|
125
130
|
--focus-ring-offset: 2px;
|
|
126
131
|
|
|
@@ -153,30 +158,32 @@
|
|
|
153
158
|
--surface-2: #ffffff;
|
|
154
159
|
--surface-3: #eaf1f7;
|
|
155
160
|
--border: #d8dfe8;
|
|
156
|
-
|
|
161
|
+
/* border-strong NO se redefine en claro: night-400 (:root) cumple 1.4.11 en
|
|
162
|
+
AMBOS temas (3.26-3.99:1 en dark, 4.27-4.87:1 en light). #b9c3d1 daba
|
|
163
|
+
1.53-1.78:1, bajo el mínimo de 3:1 para UI. */
|
|
157
164
|
--text-primary: #14171f;
|
|
158
165
|
--text-secondary: #3d4354;
|
|
159
|
-
--text-muted: #
|
|
166
|
+
--text-muted: #5f6575;
|
|
160
167
|
--text-disabled: #9aa0b0;
|
|
161
168
|
--text-heading: #0b0d12;
|
|
162
169
|
--text-on-accent: #010203; /* el CTA no cambia */
|
|
163
|
-
--success: #
|
|
164
|
-
--warning: #
|
|
165
|
-
--danger: #
|
|
166
|
-
--info: #
|
|
170
|
+
--success: #166534; --success-bg: #e3f5ea;
|
|
171
|
+
--warning: #854d0e; --warning-bg: #fbf3dd;
|
|
172
|
+
--danger: #b02c2c; --danger-bg: #fbe7e7;
|
|
173
|
+
--info: #186a94; --info-bg: #e3f2fb;
|
|
167
174
|
--accent-ghost-hover: rgba(31,135,187,.08);
|
|
168
175
|
--accent-ghost-active: rgba(31,135,187,.14);
|
|
169
176
|
--accent-selected: rgba(82,186,237,.12);
|
|
170
177
|
--overlay: rgba(20,23,31,.45);
|
|
171
178
|
|
|
172
|
-
--link: #
|
|
173
|
-
--link-hover: #
|
|
174
|
-
--focus-color: #1f87bb;
|
|
179
|
+
--link: #186a94;
|
|
180
|
+
--link-hover: #12506f;
|
|
181
|
+
--focus-color: #1f87bb; /* UI (4.01:1 ≥ 3), no texto */
|
|
175
182
|
--border-hover: #b9c3d1;
|
|
176
183
|
--outline-hover-bg: #eaf1f7;
|
|
177
184
|
--outline-active-bg: #d8dfe8;
|
|
178
185
|
--text-on-danger: #ffffff;
|
|
179
|
-
--data-color: #
|
|
186
|
+
--data-color: #186a94; /* cifras y .progress__value: es texto */
|
|
180
187
|
--elevated-border: #d8dfe8;
|
|
181
188
|
--card-border: #d8dfe8;
|
|
182
189
|
--code-color: #186a94;
|
|
@@ -387,6 +394,8 @@ a:hover { color: var(--link-hover); }
|
|
|
387
394
|
============================================================ */
|
|
388
395
|
.field { display: flex; flex-direction: column; margin-bottom: var(--form-gap); }
|
|
389
396
|
.field__label { font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); margin-bottom: 6px; }
|
|
397
|
+
/* Campo obligatorio: asterisco decorativo; la semántica va en required/aria-required del control */
|
|
398
|
+
.field--required > .field__label::after { content: "*"; margin-left: 2px; color: var(--danger); font-weight: 600; }
|
|
390
399
|
.field__help { font-size: var(--text-sm); color: var(--text-muted); margin-top: 6px; }
|
|
391
400
|
.field__error {
|
|
392
401
|
display: inline-flex; align-items: center; gap: 6px;
|
|
@@ -422,7 +431,7 @@ a:hover { color: var(--link-hover); }
|
|
|
422
431
|
.input-wrap .input { padding-left: 40px; }
|
|
423
432
|
|
|
424
433
|
.textarea { min-height: 96px; padding: 12px 14px; resize: vertical; line-height: var(--leading-body); }
|
|
425
|
-
.field__count { font-size: var(--text-xs); color: var(--text-
|
|
434
|
+
.field__count { font-size: var(--text-xs); color: var(--text-muted); text-align: right; margin-top: 4px; }
|
|
426
435
|
|
|
427
436
|
/* Select nativo estilizado (el menú custom usa .menu) */
|
|
428
437
|
.select-wrap { position: relative; }
|
|
@@ -530,6 +539,12 @@ a:hover { color: var(--link-hover); }
|
|
|
530
539
|
margin-top: -2px;
|
|
531
540
|
}
|
|
532
541
|
.checkbox:checked::after { transform: rotate(-45deg) scale(1); }
|
|
542
|
+
/* Indeterminate: guion horizontal, no el check. Solo por JS (el.indeterminate = true) */
|
|
543
|
+
.checkbox:indeterminate { background: var(--accent-400); border-color: var(--accent-400); }
|
|
544
|
+
.checkbox:indeterminate::after {
|
|
545
|
+
content: ""; width: 10px; height: 2px; margin-top: 0;
|
|
546
|
+
border: 0; border-radius: var(--radius-full); background: var(--text-on-accent); transform: none;
|
|
547
|
+
}
|
|
533
548
|
.radio::after {
|
|
534
549
|
content: ""; width: 8px; height: 8px; border-radius: var(--radius-full);
|
|
535
550
|
background: var(--text-on-accent); transform: scale(0);
|
|
@@ -613,6 +628,17 @@ a:hover { color: var(--link-hover); }
|
|
|
613
628
|
.avatar__dot--away { background: var(--warning); }
|
|
614
629
|
.avatar__dot--offline { background: var(--border); }
|
|
615
630
|
|
|
631
|
+
/* AvatarGroup — avatares solapados + chip +N.
|
|
632
|
+
Borde 2px del color del fondo (--surface-1 en tarjetas, --bg en el lienzo) para separar. */
|
|
633
|
+
.avatar-group { display: inline-flex; align-items: center; }
|
|
634
|
+
.avatar-group .avatar { border: 2px solid var(--surface-1); }
|
|
635
|
+
.avatar-group .avatar:not(:first-child) { margin-left: -8px; }
|
|
636
|
+
.avatar-group__more {
|
|
637
|
+
background: var(--surface-3); color: var(--text-secondary);
|
|
638
|
+
font-family: var(--font-body); font-weight: 600;
|
|
639
|
+
letter-spacing: 0; text-transform: none;
|
|
640
|
+
}
|
|
641
|
+
|
|
616
642
|
/* ============================================================
|
|
617
643
|
12B. DIVIDER — .divider (hairline), .divider--label, .divider--vertical
|
|
618
644
|
============================================================ */
|
|
@@ -648,6 +674,32 @@ a:hover { color: var(--link-hover); }
|
|
|
648
674
|
.stat__delta--down { color: var(--danger); }
|
|
649
675
|
.stat__delta--neutral { color: var(--text-muted); }
|
|
650
676
|
|
|
677
|
+
/* Chip de métrica: rótulo y valor en una pastilla de una línea */
|
|
678
|
+
.metric-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
679
|
+
.metric-chip {
|
|
680
|
+
display: inline-flex; align-items: center; gap: var(--space-2);
|
|
681
|
+
padding: var(--space-1) var(--space-3);
|
|
682
|
+
border: 1px solid var(--border); border-radius: var(--radius-full);
|
|
683
|
+
background: var(--surface-1);
|
|
684
|
+
font-family: var(--font-body); font-size: var(--text-sm); line-height: var(--leading-snug);
|
|
685
|
+
color: var(--text-secondary); white-space: nowrap;
|
|
686
|
+
}
|
|
687
|
+
.metric-chip svg { width: 16px; height: 16px; flex: none; color: var(--text-muted); }
|
|
688
|
+
.metric-chip__value {
|
|
689
|
+
font-family: var(--font-mono); font-weight: 500; color: var(--link);
|
|
690
|
+
font-variant-numeric: tabular-nums;
|
|
691
|
+
}
|
|
692
|
+
button.metric-chip {
|
|
693
|
+
min-height: var(--touch-target); padding-inline: var(--space-4);
|
|
694
|
+
cursor: pointer; text-align: left;
|
|
695
|
+
transition: background-color var(--duration-fast) var(--ease-out),
|
|
696
|
+
border-color var(--duration-fast) var(--ease-out),
|
|
697
|
+
transform var(--duration-instant) var(--ease-out);
|
|
698
|
+
}
|
|
699
|
+
button.metric-chip:hover { background: var(--outline-hover-bg); border-color: var(--border-hover); }
|
|
700
|
+
button.metric-chip:active { background: var(--outline-active-bg); transform: scale(.98); }
|
|
701
|
+
button.metric-chip:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
|
|
702
|
+
|
|
651
703
|
/* ============================================================
|
|
652
704
|
13B. PAGINATION — .pagination (activa: fondo acento + peso, no solo color)
|
|
653
705
|
============================================================ */
|
|
@@ -730,7 +782,7 @@ a:hover { color: var(--link-hover); }
|
|
|
730
782
|
}
|
|
731
783
|
.alert > svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
|
|
732
784
|
.alert__title { font-weight: 600; margin: 0 0 2px; color: inherit; }
|
|
733
|
-
.alert__body { margin: 0; color: inherit;
|
|
785
|
+
.alert__body { margin: 0; color: inherit; }
|
|
734
786
|
.alert__close {
|
|
735
787
|
margin-left: auto; flex: none; display: grid; place-items: center;
|
|
736
788
|
width: 36px; height: 36px; border: 0; border-radius: var(--radius-sm);
|
|
@@ -820,9 +872,39 @@ a:hover { color: var(--link-hover); }
|
|
|
820
872
|
}
|
|
821
873
|
.table tbody tr { transition: background-color var(--duration-fast) var(--ease-out); }
|
|
822
874
|
.table tbody tr:hover { background: var(--row-hover); }
|
|
823
|
-
.table tbody tr.is-selected { background: var(--accent-selected); box-shadow: inset 2px 0 0 var(--accent-400); }
|
|
824
875
|
.table .num, .table td.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
825
876
|
|
|
877
|
+
/* Zebra — filas pares en surface-2; hover y selección mandan sobre la banda */
|
|
878
|
+
.table--zebra tbody tr:nth-child(even) { background: var(--surface-2); }
|
|
879
|
+
.table--zebra tbody tr:hover { background: var(--row-hover); }
|
|
880
|
+
|
|
881
|
+
/* Sticky header — dentro de un contenedor con overflow: auto y altura acotada */
|
|
882
|
+
.table--sticky thead th { position: sticky; top: 0; z-index: 1; }
|
|
883
|
+
|
|
884
|
+
/* Header ordenable — botón dentro del th + icono; el estado vive en aria-sort del th */
|
|
885
|
+
.th-sort {
|
|
886
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
887
|
+
margin: 0; padding: 0; border: 0; background: transparent;
|
|
888
|
+
font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
|
|
889
|
+
cursor: pointer;
|
|
890
|
+
}
|
|
891
|
+
.th-sort:hover { color: var(--text-secondary); }
|
|
892
|
+
.th-sort:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-radius: var(--radius-sm); }
|
|
893
|
+
.sort-icon {
|
|
894
|
+
width: 14px; height: 14px; flex: none; opacity: .45;
|
|
895
|
+
transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out);
|
|
896
|
+
}
|
|
897
|
+
.table th[aria-sort="ascending"] .sort-icon,
|
|
898
|
+
.table th[aria-sort="descending"] .sort-icon { opacity: 1; color: var(--accent-400); }
|
|
899
|
+
.table th[aria-sort="descending"] .sort-icon { transform: rotate(180deg); }
|
|
900
|
+
|
|
901
|
+
/* Columna de selección (checkbox en th y en cada fila) */
|
|
902
|
+
.table .col-select { width: 44px; text-align: center; padding-left: var(--space-2); padding-right: var(--space-2); }
|
|
903
|
+
|
|
904
|
+
/* Fila seleccionada — .row--selected (contrato); .is-selected se mantiene como alias */
|
|
905
|
+
.table tbody tr.is-selected,
|
|
906
|
+
.table tbody tr.row--selected { background: var(--accent-selected); box-shadow: inset 2px 0 0 var(--accent-400); }
|
|
907
|
+
|
|
826
908
|
/* ============================================================
|
|
827
909
|
19. NAVEGACIÓN — .navbar, .sidebar, .tabs, .breadcrumbs
|
|
828
910
|
============================================================ */
|
|
@@ -862,6 +944,70 @@ a:hover { color: var(--link-hover); }
|
|
|
862
944
|
.sidebar__item svg { width: 18px; height: 18px; color: var(--text-muted); flex: none; }
|
|
863
945
|
.sidebar__item.is-active svg { color: var(--sidebar-active-icon); }
|
|
864
946
|
|
|
947
|
+
/* Grupos con rótulo dentro del sidebar */
|
|
948
|
+
.sidebar__group { display: flex; flex-direction: column; gap: 2px; }
|
|
949
|
+
.sidebar__group + .sidebar__group { margin-top: var(--space-4); }
|
|
950
|
+
.sidebar__group-label {
|
|
951
|
+
padding: 0 12px var(--space-1);
|
|
952
|
+
font-size: var(--text-xs); font-weight: 500; text-transform: uppercase;
|
|
953
|
+
letter-spacing: var(--tracking-overline); color: var(--text-muted); white-space: nowrap;
|
|
954
|
+
}
|
|
955
|
+
.sidebar__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
956
|
+
|
|
957
|
+
/* Sidebar contraíble: expandido 260 px, riel de iconos de 64 px.
|
|
958
|
+
El ancho cambia sin transición; solo los rótulos se funden con opacity y transform. */
|
|
959
|
+
.sidebar--collapsible .sidebar__label {
|
|
960
|
+
transition: opacity var(--duration-fast) var(--ease-out),
|
|
961
|
+
transform var(--duration-fast) var(--ease-out);
|
|
962
|
+
}
|
|
963
|
+
.sidebar__toggle {
|
|
964
|
+
margin-top: auto; width: 100%; border: 0; background: transparent;
|
|
965
|
+
font-family: var(--font-body); text-align: left; cursor: pointer;
|
|
966
|
+
}
|
|
967
|
+
.sidebar.is-collapsed { width: 64px; }
|
|
968
|
+
.sidebar.is-collapsed .sidebar__item { position: relative; justify-content: center; padding-inline: 0; }
|
|
969
|
+
.sidebar.is-collapsed .sidebar__toggle svg { transform: rotate(180deg); }
|
|
970
|
+
.sidebar.is-collapsed .sidebar__group + .sidebar__group { margin-top: var(--space-2); }
|
|
971
|
+
/* En el riel, el rótulo de grupo es un separador de 1 px; su texto sigue en el árbol de accesibilidad */
|
|
972
|
+
.sidebar.is-collapsed .sidebar__group-label {
|
|
973
|
+
height: 1px; padding: 0; margin: 0 var(--space-2) var(--space-2);
|
|
974
|
+
overflow: hidden; color: transparent; background: var(--border);
|
|
975
|
+
}
|
|
976
|
+
/* Oculto a la vista pero accesible (patrón de .visually-hidden) */
|
|
977
|
+
.sidebar.is-collapsed .sidebar__label,
|
|
978
|
+
.sidebar.is-collapsed .sidebar__group:first-child > .sidebar__group-label {
|
|
979
|
+
position: absolute; width: 1px; height: 1px; margin: -1px;
|
|
980
|
+
clip-path: inset(50%); overflow: hidden; white-space: nowrap;
|
|
981
|
+
}
|
|
982
|
+
.sidebar.is-collapsed .sidebar__label {
|
|
983
|
+
background: var(--tooltip-bg); border: 1px solid var(--tooltip-border); border-radius: var(--radius-sm);
|
|
984
|
+
color: var(--tooltip-text); font-size: var(--text-xs); line-height: 16px;
|
|
985
|
+
pointer-events: none; z-index: 70;
|
|
986
|
+
opacity: 0; transform: translateX(-4px);
|
|
987
|
+
}
|
|
988
|
+
/* Tooltip del riel: el propio rótulo aparece a la derecha del ítem, también con teclado */
|
|
989
|
+
.sidebar.is-collapsed .sidebar__item:focus-visible .sidebar__label {
|
|
990
|
+
left: calc(100% + 8px); top: calc(50% - 15px); width: auto; height: auto;
|
|
991
|
+
margin: 0; padding: 6px 10px; clip-path: none; overflow: visible;
|
|
992
|
+
opacity: 1; transform: none; transition-delay: 400ms;
|
|
993
|
+
}
|
|
994
|
+
/* Por hover solo con puntero real — en touch quedaría pegado tras el tap */
|
|
995
|
+
@media (hover: hover) and (pointer: fine) {
|
|
996
|
+
.sidebar.is-collapsed .sidebar__item:hover .sidebar__label {
|
|
997
|
+
left: calc(100% + 8px); top: calc(50% - 15px); width: auto; height: auto;
|
|
998
|
+
margin: 0; padding: 6px 10px; clip-path: none; overflow: visible;
|
|
999
|
+
opacity: 1; transform: none; transition-delay: 400ms;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
/* Por debajo de 768 px la navegación vive en un Drawer --left, que abre el botón ☰
|
|
1003
|
+
.sidebar-trigger (combinado con .icon-btn); desde 768 px el sidebar es fijo y el botón sobra */
|
|
1004
|
+
@media (max-width: 767px) {
|
|
1005
|
+
.sidebar--collapsible { display: none; }
|
|
1006
|
+
}
|
|
1007
|
+
@media (min-width: 768px) {
|
|
1008
|
+
.sidebar-trigger { display: none; }
|
|
1009
|
+
}
|
|
1010
|
+
|
|
865
1011
|
.tabs { position: relative; display: flex; gap: var(--space-2); border-bottom: 1px solid var(--border-strong); }
|
|
866
1012
|
.tabs__btn {
|
|
867
1013
|
padding: 10px 14px; border: 0; background: transparent; cursor: pointer;
|
|
@@ -1007,6 +1153,273 @@ a:hover { color: var(--link-hover); }
|
|
|
1007
1153
|
clip-path: inset(50%); overflow: hidden; white-space: nowrap;
|
|
1008
1154
|
}
|
|
1009
1155
|
|
|
1156
|
+
/* ============================================================
|
|
1157
|
+
25. COMBOBOX / AUTOCOMPLETE — .combobox + SEARCH — .search
|
|
1158
|
+
El listado vive sobre --surface-2 (superficie de dropdowns).
|
|
1159
|
+
============================================================ */
|
|
1160
|
+
.combobox { position: relative; }
|
|
1161
|
+
.combobox__input { width: 100%; } /* consume .input para el resto de estilos */
|
|
1162
|
+
.combobox__list {
|
|
1163
|
+
position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 40;
|
|
1164
|
+
margin: 0; padding: 6px; list-style: none; max-height: 264px; overflow: auto;
|
|
1165
|
+
background: var(--surface-2); border: 1px solid var(--elevated-border);
|
|
1166
|
+
border-radius: var(--radius-md); box-shadow: var(--shadow-3);
|
|
1167
|
+
}
|
|
1168
|
+
.combobox__list[hidden] { display: none; }
|
|
1169
|
+
.combobox__option {
|
|
1170
|
+
display: flex; align-items: center; gap: 10px; width: 100%;
|
|
1171
|
+
padding: 10px 12px; border-radius: var(--radius-sm);
|
|
1172
|
+
color: var(--text-primary); font-size: var(--text-sm); cursor: pointer;
|
|
1173
|
+
transition: background-color var(--duration-fast) var(--ease-out);
|
|
1174
|
+
}
|
|
1175
|
+
.combobox__option[hidden] { display: none; }
|
|
1176
|
+
/* .is-active = resaltado por teclado; [aria-selected] = la elegida (fondo + icono, no solo color) */
|
|
1177
|
+
.combobox__option.is-active { background: var(--surface-3); }
|
|
1178
|
+
.combobox__option[aria-selected="true"] { background: var(--accent-selected); color: var(--accent-300); }
|
|
1179
|
+
[data-theme="light"] .combobox__option[aria-selected="true"] { color: var(--link); }
|
|
1180
|
+
.combobox__check { display: none; margin-left: auto; width: 16px; height: 16px; color: var(--accent-400); flex: none; }
|
|
1181
|
+
.combobox__option[aria-selected="true"] .combobox__check { display: block; }
|
|
1182
|
+
|
|
1183
|
+
/* Search — wrapper con lupa (aria-hidden) + botón limpiar (×, aria-label="Limpiar") */
|
|
1184
|
+
.search { position: relative; display: flex; align-items: center; }
|
|
1185
|
+
.search__icon {
|
|
1186
|
+
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
|
|
1187
|
+
width: 18px; height: 18px; color: var(--text-disabled); pointer-events: none;
|
|
1188
|
+
}
|
|
1189
|
+
.search__input { width: 100%; padding-left: 40px; padding-right: 40px; } /* consume .input */
|
|
1190
|
+
/* Oculta el clear nativo de WebKit para no duplicar el affordance del .search__clear */
|
|
1191
|
+
.search__input::-webkit-search-cancel-button,
|
|
1192
|
+
.search__input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
|
|
1193
|
+
.search__clear {
|
|
1194
|
+
position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
|
|
1195
|
+
display: inline-grid; place-content: center; width: 28px; height: 28px; padding: 0;
|
|
1196
|
+
border: 0; border-radius: var(--radius-full); background: transparent;
|
|
1197
|
+
color: var(--text-muted); cursor: pointer;
|
|
1198
|
+
transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
|
|
1199
|
+
}
|
|
1200
|
+
.search__clear:hover { background: var(--surface-3); color: var(--text-primary); }
|
|
1201
|
+
.search__clear svg { width: 16px; height: 16px; }
|
|
1202
|
+
.search__clear[hidden] { display: none; }
|
|
1203
|
+
|
|
1204
|
+
/* ============================================================
|
|
1205
|
+
26. DRAWER / SHEET — .drawer + __overlay + __panel (focus-trap por JS)
|
|
1206
|
+
Motion: transform translate + fade del overlay (--duration-slow).
|
|
1207
|
+
El overlay reutiliza --overlay del modal; SIN blur nuevo.
|
|
1208
|
+
============================================================ */
|
|
1209
|
+
.drawer { position: fixed; inset: 0; z-index: 100; }
|
|
1210
|
+
.drawer[hidden] { display: none; }
|
|
1211
|
+
.drawer__overlay {
|
|
1212
|
+
position: absolute; inset: 0; background: var(--overlay);
|
|
1213
|
+
opacity: 0; transition: opacity var(--duration-slow) var(--ease-out);
|
|
1214
|
+
}
|
|
1215
|
+
.drawer.is-open .drawer__overlay { opacity: 1; }
|
|
1216
|
+
.drawer__panel {
|
|
1217
|
+
position: absolute; top: 0; right: 0; bottom: 0;
|
|
1218
|
+
display: flex; flex-direction: column;
|
|
1219
|
+
width: min(400px, 100%); padding: var(--card-padding);
|
|
1220
|
+
background: var(--surface-2); border-left: 1px solid var(--elevated-border);
|
|
1221
|
+
box-shadow: var(--shadow-3);
|
|
1222
|
+
overflow-y: auto; overscroll-behavior: contain;
|
|
1223
|
+
transform: translateX(100%);
|
|
1224
|
+
transition: transform var(--duration-slow) var(--ease-out);
|
|
1225
|
+
}
|
|
1226
|
+
.drawer.is-open .drawer__panel { transform: none; }
|
|
1227
|
+
/* Variante izquierda */
|
|
1228
|
+
.drawer--left .drawer__panel {
|
|
1229
|
+
right: auto; left: 0; border-left: 0; border-right: 1px solid var(--elevated-border);
|
|
1230
|
+
transform: translateX(-100%);
|
|
1231
|
+
}
|
|
1232
|
+
.drawer--left.is-open .drawer__panel { transform: none; }
|
|
1233
|
+
/* Variante inferior (sheet) */
|
|
1234
|
+
.drawer--bottom .drawer__panel {
|
|
1235
|
+
top: auto; left: 0; right: 0; width: 100%; max-height: 80dvh;
|
|
1236
|
+
border-left: 0; border-top: 1px solid var(--elevated-border);
|
|
1237
|
+
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
|
1238
|
+
transform: translateY(100%);
|
|
1239
|
+
}
|
|
1240
|
+
.drawer--bottom.is-open .drawer__panel { transform: none; }
|
|
1241
|
+
.drawer__header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); }
|
|
1242
|
+
.drawer__title { font-family: var(--font-body); font-size: var(--text-xl); font-weight: 600; letter-spacing: normal; color: var(--text-heading); margin: 0; }
|
|
1243
|
+
.drawer__body { flex: 1; }
|
|
1244
|
+
.drawer__footer { display: flex; justify-content: flex-end; gap: var(--space-3); margin-top: var(--space-6); }
|
|
1245
|
+
|
|
1246
|
+
/* ============================================================
|
|
1247
|
+
27. POPOVER — .popover + __trigger + __panel (abre por CLICK)
|
|
1248
|
+
Motion: fade + scale(0.96→1) desde el transform-origin del trigger.
|
|
1249
|
+
Panel sobre --surface-2. Se distingue del Tooltip: click, no hover.
|
|
1250
|
+
============================================================ */
|
|
1251
|
+
.popover { position: relative; display: inline-flex; }
|
|
1252
|
+
.popover__panel {
|
|
1253
|
+
position: absolute; top: calc(100% + 8px); left: 0; z-index: 60;
|
|
1254
|
+
min-width: 240px; max-width: 320px; padding: var(--space-4);
|
|
1255
|
+
background: var(--surface-2); border: 1px solid var(--elevated-border);
|
|
1256
|
+
border-radius: var(--radius-md); box-shadow: var(--shadow-3);
|
|
1257
|
+
transform-origin: top left; /* origin-aware: crece desde el disparador */
|
|
1258
|
+
opacity: 0; transform: scale(.96); visibility: hidden;
|
|
1259
|
+
transition: opacity var(--duration-base) var(--ease-out),
|
|
1260
|
+
transform var(--duration-base) var(--ease-out),
|
|
1261
|
+
visibility 0s linear var(--duration-base);
|
|
1262
|
+
}
|
|
1263
|
+
.popover.is-open .popover__panel {
|
|
1264
|
+
opacity: 1; transform: none; visibility: visible;
|
|
1265
|
+
transition: opacity var(--duration-base) var(--ease-out),
|
|
1266
|
+
transform var(--duration-base) var(--ease-out),
|
|
1267
|
+
visibility 0s;
|
|
1268
|
+
}
|
|
1269
|
+
/* Alineado a la derecha del disparador */
|
|
1270
|
+
.popover--right .popover__panel { left: auto; right: 0; transform-origin: top right; }
|
|
1271
|
+
|
|
1272
|
+
/* ============================================================
|
|
1273
|
+
28. BUTTON GROUP — .btn-group (bordes compartidos, radios en extremos)
|
|
1274
|
+
============================================================ */
|
|
1275
|
+
.btn-group { display: inline-flex; }
|
|
1276
|
+
.btn-group > .btn { border-radius: 0; }
|
|
1277
|
+
.btn-group > .btn:not(:first-child) { margin-left: -1px; }
|
|
1278
|
+
.btn-group > .btn:first-child { border-top-left-radius: var(--radius-md); border-bottom-left-radius: var(--radius-md); }
|
|
1279
|
+
.btn-group > .btn:last-child { border-top-right-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
|
|
1280
|
+
/* El botón enfocado/hover sube de z-index para que su borde no lo tape el vecino */
|
|
1281
|
+
.btn-group > .btn:hover, .btn-group > .btn:focus-visible, .btn-group > .btn.is-active { z-index: 1; }
|
|
1282
|
+
|
|
1283
|
+
/* ============================================================
|
|
1284
|
+
29. DATEPICKER — .datepicker + calendario en popover
|
|
1285
|
+
El panel reutiliza el look de .popover__panel sobre --surface-2.
|
|
1286
|
+
Grid role="grid"; días como <button role="gridcell">.
|
|
1287
|
+
============================================================ */
|
|
1288
|
+
.datepicker { position: relative; }
|
|
1289
|
+
.datepicker__input { cursor: pointer; } /* consume .input; el icono va con .input-wrap */
|
|
1290
|
+
.datepicker__calendar {
|
|
1291
|
+
position: absolute; top: calc(100% + 8px); left: 0; z-index: 60;
|
|
1292
|
+
padding: var(--space-4);
|
|
1293
|
+
background: var(--surface-2); border: 1px solid var(--elevated-border);
|
|
1294
|
+
border-radius: var(--radius-md); box-shadow: var(--shadow-3);
|
|
1295
|
+
}
|
|
1296
|
+
.datepicker__calendar[hidden] { display: none; }
|
|
1297
|
+
|
|
1298
|
+
.calendar { width: 252px; }
|
|
1299
|
+
.calendar__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }
|
|
1300
|
+
.calendar__title { font-size: var(--text-sm); font-weight: 600; color: var(--text-heading); } /* mes en minúscula (es) */
|
|
1301
|
+
.calendar__weekdays { display: grid; grid-template-columns: repeat(7, 1fr); margin-bottom: var(--space-1); }
|
|
1302
|
+
.calendar__weekdays span {
|
|
1303
|
+
display: grid; place-items: center; height: 28px;
|
|
1304
|
+
font-size: var(--text-xs); font-weight: 500; color: var(--text-muted);
|
|
1305
|
+
}
|
|
1306
|
+
.calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
|
|
1307
|
+
.calendar__week { display: contents; } /* fila semántica (role="row") sin romper el grid */
|
|
1308
|
+
.calendar__day {
|
|
1309
|
+
display: grid; place-items: center; width: 100%; height: 36px; padding: 0;
|
|
1310
|
+
border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent;
|
|
1311
|
+
color: var(--text-primary); font-family: var(--font-mono); font-size: var(--text-sm);
|
|
1312
|
+
font-variant-numeric: tabular-nums; cursor: pointer;
|
|
1313
|
+
transition: background-color var(--duration-fast) var(--ease-out),
|
|
1314
|
+
color var(--duration-fast) var(--ease-out);
|
|
1315
|
+
}
|
|
1316
|
+
.calendar__day:hover:not([disabled]):not(.calendar__day--selected) { background: var(--surface-3); }
|
|
1317
|
+
/* Hoy: aro de acento por borde (no glow) + texto de acento */
|
|
1318
|
+
.calendar__day--today { border-color: var(--accent-400); color: var(--accent-300); }
|
|
1319
|
+
/* Seleccionado: fondo de acento + texto oscuro + peso — no depende solo del color */
|
|
1320
|
+
.calendar__day--selected { background: var(--accent-400); border-color: var(--accent-400); color: var(--text-on-accent); font-weight: 600; }
|
|
1321
|
+
.calendar__day--outside { color: var(--text-muted); }
|
|
1322
|
+
.calendar__day[disabled] { color: var(--text-disabled); opacity: .5; cursor: not-allowed; }
|
|
1323
|
+
|
|
1324
|
+
/* ============================================================
|
|
1325
|
+
30. FILE UPLOAD — .file-upload (dropzone) + __list
|
|
1326
|
+
El input file real va oculto accesiblemente (.visually-hidden).
|
|
1327
|
+
============================================================ */
|
|
1328
|
+
.file-upload { display: block; }
|
|
1329
|
+
.file-upload__label {
|
|
1330
|
+
display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
|
|
1331
|
+
padding: var(--space-8) var(--space-6); text-align: center;
|
|
1332
|
+
background: var(--surface-1); border: 1px dashed var(--border-strong);
|
|
1333
|
+
border-radius: var(--radius-lg); color: var(--text-muted); cursor: pointer;
|
|
1334
|
+
transition: border-color var(--duration-fast) var(--ease-out),
|
|
1335
|
+
background-color var(--duration-fast) var(--ease-out),
|
|
1336
|
+
color var(--duration-fast) var(--ease-out);
|
|
1337
|
+
}
|
|
1338
|
+
.file-upload__label > svg { width: 28px; height: 28px; color: var(--text-muted); }
|
|
1339
|
+
.file-upload__button { color: var(--link); font-weight: 600; }
|
|
1340
|
+
.file-upload__hint { font-size: var(--text-xs); color: var(--text-muted); }
|
|
1341
|
+
@media (hover: hover) and (pointer: fine) {
|
|
1342
|
+
.file-upload__label:hover { border-color: var(--border-hover); background: var(--surface-2); color: var(--text-secondary); }
|
|
1343
|
+
}
|
|
1344
|
+
/* El ring se pinta sobre el label porque el input está oculto */
|
|
1345
|
+
.file-upload__input:focus-visible + .file-upload__label { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
|
|
1346
|
+
/* Arrastrando encima: acento (icono + borde, no solo color) */
|
|
1347
|
+
.file-upload.is-dragover .file-upload__label { border-color: var(--accent-400); background: var(--accent-ghost-hover); color: var(--text-secondary); }
|
|
1348
|
+
|
|
1349
|
+
.file-upload__list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
|
|
1350
|
+
.file-upload__item {
|
|
1351
|
+
display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--space-3);
|
|
1352
|
+
padding: var(--space-3); background: var(--surface-2);
|
|
1353
|
+
border: 1px solid var(--border); border-radius: var(--radius-md);
|
|
1354
|
+
}
|
|
1355
|
+
.file-upload__item > svg { width: 20px; height: 20px; color: var(--text-muted); flex: none; }
|
|
1356
|
+
.file-upload__item > div { min-width: 0; } /* permite truncar el nombre */
|
|
1357
|
+
.file-upload__item .progress { margin-top: var(--space-2); }
|
|
1358
|
+
.file-upload__name { display: block; font-size: var(--text-sm); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1359
|
+
.file-upload__size { display: block; font-size: var(--text-xs); color: var(--text-muted); font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
1360
|
+
/* Error: borde + icono + texto en danger, nunca solo por color */
|
|
1361
|
+
.file-upload__item--error { border-color: color-mix(in srgb, var(--danger) 45%, transparent); background: var(--danger-bg); }
|
|
1362
|
+
.file-upload__item--error > svg { color: var(--danger); }
|
|
1363
|
+
.file-upload__item--error .file-upload__size { color: var(--danger); }
|
|
1364
|
+
.file-upload__remove {
|
|
1365
|
+
display: inline-grid; place-content: center; width: 28px; height: 28px; padding: 0; flex: none;
|
|
1366
|
+
border: 0; border-radius: var(--radius-full); background: transparent;
|
|
1367
|
+
color: var(--text-muted); cursor: pointer;
|
|
1368
|
+
transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
|
|
1369
|
+
}
|
|
1370
|
+
.file-upload__remove:hover { background: var(--surface-3); color: var(--text-primary); }
|
|
1371
|
+
.file-upload__remove svg { width: 16px; height: 16px; }
|
|
1372
|
+
|
|
1373
|
+
/* ============================================================
|
|
1374
|
+
31. STEPPER / WIZARD — .stepper (<ol>) + .step
|
|
1375
|
+
Completo → check (no solo color); actual → acento.
|
|
1376
|
+
============================================================ */
|
|
1377
|
+
.stepper { display: flex; list-style: none; margin: 0; padding: 0; }
|
|
1378
|
+
.step {
|
|
1379
|
+
position: relative; flex: 1; display: flex; flex-direction: column; align-items: center;
|
|
1380
|
+
gap: var(--space-2); text-align: center; min-width: 0;
|
|
1381
|
+
}
|
|
1382
|
+
.step__marker {
|
|
1383
|
+
position: relative; z-index: 1;
|
|
1384
|
+
display: grid; place-items: center; width: 32px; height: 32px; flex: none;
|
|
1385
|
+
border-radius: var(--radius-full); border: 2px solid var(--border);
|
|
1386
|
+
background: var(--surface-1); color: var(--text-muted);
|
|
1387
|
+
font-family: var(--font-mono); font-size: var(--text-sm); font-weight: 600; font-variant-numeric: tabular-nums;
|
|
1388
|
+
}
|
|
1389
|
+
.step__marker svg { width: 16px; height: 16px; }
|
|
1390
|
+
.step__label { font-size: var(--text-sm); color: var(--text-muted); }
|
|
1391
|
+
.step__connector {
|
|
1392
|
+
position: absolute; top: 15px; left: 50%; z-index: 0;
|
|
1393
|
+
width: 100%; height: 2px; background: var(--border);
|
|
1394
|
+
}
|
|
1395
|
+
/* Completo */
|
|
1396
|
+
.step--complete .step__marker { background: var(--accent-400); border-color: var(--accent-400); color: var(--text-on-accent); }
|
|
1397
|
+
.step--complete .step__label { color: var(--text-secondary); }
|
|
1398
|
+
.step--complete .step__connector { background: var(--accent-400); }
|
|
1399
|
+
/* Actual */
|
|
1400
|
+
.step--current .step__marker { border-color: var(--accent-400); color: var(--accent-300); }
|
|
1401
|
+
.step--current .step__label { color: var(--text-primary); font-weight: 600; }
|
|
1402
|
+
/* Próximo = valores por defecto (atenuado) */
|
|
1403
|
+
|
|
1404
|
+
/* Variante vertical */
|
|
1405
|
+
.stepper--vertical { flex-direction: column; }
|
|
1406
|
+
.stepper--vertical .step { flex-direction: row; align-items: flex-start; gap: var(--space-3); text-align: left; padding-bottom: var(--space-6); }
|
|
1407
|
+
.stepper--vertical .step:last-child { padding-bottom: 0; }
|
|
1408
|
+
.stepper--vertical .step__label { padding-top: var(--space-1); }
|
|
1409
|
+
.stepper--vertical .step__connector { top: 34px; left: 15px; width: 2px; height: calc(100% - 34px); }
|
|
1410
|
+
|
|
1411
|
+
/* ============================================================
|
|
1412
|
+
32. KBD — .kbd (tecla / atajo de teclado)
|
|
1413
|
+
Combos: varias .kbd con un "+" literal entre ellas.
|
|
1414
|
+
============================================================ */
|
|
1415
|
+
.kbd {
|
|
1416
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
1417
|
+
min-width: 20px; height: 20px; padding: 0 6px;
|
|
1418
|
+
font-family: var(--font-mono); font-size: var(--text-xs); font-weight: 500; line-height: 1;
|
|
1419
|
+
color: var(--text-secondary); background: var(--surface-2);
|
|
1420
|
+
border: 1px solid var(--border-strong); border-radius: 6px; box-shadow: var(--shadow-1);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1010
1423
|
|
|
1011
1424
|
/* ============================================================
|
|
1012
1425
|
ESTILOS DE LA PÁGINA DE DOCS (.ds-*)
|
|
@@ -1071,10 +1484,23 @@ html { scroll-behavior: smooth; }
|
|
|
1071
1484
|
.ds-demo--grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); align-items: start; }
|
|
1072
1485
|
.ds-demo:has(table) { overflow-x: auto; }
|
|
1073
1486
|
|
|
1487
|
+
/* Demo del sidebar contraíble: rejilla auto 1fr, el contenido sigue al riel sin animar el ancho */
|
|
1488
|
+
.ds-app-demo { display: grid; grid-template-columns: auto 1fr; align-items: stretch; gap: 0; min-height: 400px; padding: 0; }
|
|
1489
|
+
.ds-app-demo > .sidebar { border-radius: var(--radius-lg) 0 0 var(--radius-lg); }
|
|
1490
|
+
.ds-app-demo__main { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); min-width: 0; padding: var(--space-6); }
|
|
1491
|
+
@media (max-width: 767px) {
|
|
1492
|
+
.ds-app-demo { grid-template-columns: 1fr; min-height: 0; }
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1074
1495
|
.ds-code { margin-top: var(--space-3); border: 1px solid var(--row-border); border-radius: var(--radius-md); overflow: hidden; }
|
|
1496
|
+
/* «Copiar» es hermano del details, no hijo del summary (un control no puede
|
|
1497
|
+
anidarse en otro), y se superpone al extremo derecho de la cabecera. */
|
|
1498
|
+
.ds-code-wrap { position: relative; margin-top: var(--space-3); }
|
|
1499
|
+
.ds-code-wrap > .ds-code { margin-top: 0; }
|
|
1500
|
+
.ds-code-wrap > .ds-copy { position: absolute; top: 11px; right: 17px; }
|
|
1075
1501
|
.ds-code summary {
|
|
1076
1502
|
display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
|
|
1077
|
-
padding: 10px 16px; cursor: pointer; user-select: none;
|
|
1503
|
+
min-height: 45px; padding: 10px 96px 10px 16px; cursor: pointer; user-select: none;
|
|
1078
1504
|
font-size: var(--text-sm); color: var(--text-muted); list-style: none;
|
|
1079
1505
|
}
|
|
1080
1506
|
.ds-code summary::-webkit-details-marker { display: none; }
|
|
@@ -1220,6 +1646,14 @@ html { scroll-behavior: smooth; }
|
|
|
1220
1646
|
<a href="#comp-navegacion">Navegación</a>
|
|
1221
1647
|
<a href="#comp-skeleton">Skeleton y EmptyState</a>
|
|
1222
1648
|
<a href="#comp-card">Card, Link y Code</a>
|
|
1649
|
+
<a href="#comp-combobox">Combobox y Search</a>
|
|
1650
|
+
<a href="#comp-drawer">Drawer</a>
|
|
1651
|
+
<a href="#comp-popover">Popover</a>
|
|
1652
|
+
<a href="#comp-buttongroup">ButtonGroup</a>
|
|
1653
|
+
<a href="#comp-datepicker">DatePicker</a>
|
|
1654
|
+
<a href="#comp-fileupload">FileUpload</a>
|
|
1655
|
+
<a href="#comp-stepper">Stepper</a>
|
|
1656
|
+
<a href="#comp-kbd">Kbd</a>
|
|
1223
1657
|
</div>
|
|
1224
1658
|
<div class="overline ds-group">Recursos</div>
|
|
1225
1659
|
<a href="#motion">Motion</a>
|
|
@@ -1284,7 +1718,7 @@ html { scroll-behavior: smooth; }
|
|
|
1284
1718
|
<div class="ds-swatches" id="swIndigo"></div>
|
|
1285
1719
|
<div class="ds-callout ds-callout--warning">
|
|
1286
1720
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.7 18-8-14a2 2 0 0 0-3.5 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.7-3z"/><path d="M12 9v4m0 4h.01"/></svg>
|
|
1287
|
-
<div>El índigo <span class="code">#353d7c</span> sobre negro da un contraste de
|
|
1721
|
+
<div>El índigo <span class="code">#353d7c</span> sobre negro da un contraste de 2.09:1 — <strong>nunca es texto sobre fondos nocturnos</strong>, solo fondos de sección, sidebars y estados seleccionados.</div>
|
|
1288
1722
|
</div>
|
|
1289
1723
|
|
|
1290
1724
|
<h3 style="margin-top:32px;">Neutrales — Escala Nocturna</h3>
|
|
@@ -1348,11 +1782,11 @@ html { scroll-behavior: smooth; }
|
|
|
1348
1782
|
<div class="ds-elev-demo" style="background:var(--bg);border:1px dashed var(--border);">Nivel 0<br>night-950</div>
|
|
1349
1783
|
<div class="ds-elev-demo" style="background:var(--surface-1);box-shadow:var(--shadow-1);border:1px solid var(--row-border);">Nivel 1<br>night-900 + shadow-1</div>
|
|
1350
1784
|
<div class="ds-elev-demo" style="background:var(--surface-2);box-shadow:var(--shadow-2);border:1px solid var(--row-border);">Nivel 2<br>night-800 + shadow-2</div>
|
|
1351
|
-
<div class="ds-elev-demo" style="background:var(--surface-2);box-shadow:var(--shadow-3);border:1px solid var(--border-strong);">Nivel 3<br>night-800 + borde night-
|
|
1785
|
+
<div class="ds-elev-demo" style="background:var(--surface-2);box-shadow:var(--shadow-3);border:1px solid var(--border-strong);">Nivel 3<br>night-800 + borde night-400 + shadow-3</div>
|
|
1352
1786
|
</div>
|
|
1353
1787
|
<h3 style="margin-top:32px;">Glow de acento</h3>
|
|
1354
1788
|
<div class="ds-demo">
|
|
1355
|
-
<div class="ds-elev-demo" style="background:var(--surface-1);box-shadow:var(--glow-accent);max-width:280px;">glow-accent<br><span style="color:var(--text-
|
|
1789
|
+
<div class="ds-elev-demo" style="background:var(--surface-1);box-shadow:var(--glow-accent);max-width:280px;">glow-accent<br><span style="color:var(--text-muted)">máximo uno por pantalla</span></div>
|
|
1356
1790
|
</div>
|
|
1357
1791
|
</section>
|
|
1358
1792
|
|
|
@@ -1386,13 +1820,13 @@ html { scroll-behavior: smooth; }
|
|
|
1386
1820
|
<section id="componentes">
|
|
1387
1821
|
<span class="overline">Biblioteca</span>
|
|
1388
1822
|
<h2>Componentes</h2>
|
|
1389
|
-
<p class="ds-lead">Los
|
|
1823
|
+
<p class="ds-lead">Los 35 componentes del sistema, renderizados en vivo con las clases del framework <span class="code">nocturna-electrica.css</span>. Cada bloque incluye su código de uso.</p>
|
|
1390
1824
|
</section>
|
|
1391
1825
|
|
|
1392
1826
|
<!-- ================= BOTONES ================= -->
|
|
1393
1827
|
<section id="comp-botones">
|
|
1394
1828
|
<h2>Botones</h2>
|
|
1395
|
-
<p>Alturas 36 / 44 / 52px. El primario lleva <strong>texto oscuro</strong> (contraste
|
|
1829
|
+
<p>Alturas 36 / 44 / 52px. El primario lleva <strong>texto oscuro</strong> (contraste 9.50:1) — nunca blanco. Un solo primario por pantalla.</p>
|
|
1396
1830
|
<div class="ds-demo">
|
|
1397
1831
|
<button class="btn btn--primary">Guardar cambios</button>
|
|
1398
1832
|
<button class="btn btn--secondary">Ver informe</button>
|
|
@@ -1406,16 +1840,16 @@ html { scroll-behavior: smooth; }
|
|
|
1406
1840
|
<button class="btn btn--primary">Mediano</button>
|
|
1407
1841
|
<button class="btn btn--primary btn--lg">Grande</button>
|
|
1408
1842
|
<button class="btn btn--outline"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14m-7-7v14"/></svg>Con icono</button>
|
|
1409
|
-
<button class="btn btn--primary" style="min-width:160px;"><span class="spinner" style="color:currentColor;"></span></button>
|
|
1843
|
+
<button class="btn btn--primary" style="min-width:160px;" aria-label="Cargando"><span class="spinner" style="color:currentColor;"></span></button>
|
|
1410
1844
|
</div>
|
|
1411
|
-
<
|
|
1845
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><button class="btn btn--primary">Guardar cambios</button>
|
|
1412
1846
|
<button class="btn btn--secondary">Ver informe</button>
|
|
1413
1847
|
<button class="btn btn--outline">Exportar</button>
|
|
1414
1848
|
<button class="btn btn--ghost">Cancelar</button>
|
|
1415
1849
|
<button class="btn btn--danger">Eliminar cuenta</button>
|
|
1416
1850
|
|
|
1417
1851
|
<!-- Tamaños: .btn--sm (36px) · default (44px) · .btn--lg (52px) -->
|
|
1418
|
-
<!-- Loading: fijar min-width y sustituir el texto por .spinner --></code></pre></details>
|
|
1852
|
+
<!-- Loading: fijar min-width y sustituir el texto por .spinner --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1419
1853
|
</section>
|
|
1420
1854
|
|
|
1421
1855
|
<!-- ================= ICON BUTTON ================= -->
|
|
@@ -1429,10 +1863,10 @@ html { scroll-behavior: smooth; }
|
|
|
1429
1863
|
<button class="icon-btn icon-btn--outline icon-btn--sm" aria-label="Copiar"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="14" height="14" rx="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg></button>
|
|
1430
1864
|
<button class="icon-btn icon-btn--outline" disabled aria-label="Deshabilitado"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
1431
1865
|
</div>
|
|
1432
|
-
<
|
|
1866
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><button class="icon-btn icon-btn--outline" aria-label="Buscar">
|
|
1433
1867
|
<svg>…</svg>
|
|
1434
1868
|
</button>
|
|
1435
|
-
<!-- Variantes: --outline · --ghost · --accent | Tamaños: --sm (36) · default (44) · --lg (52) --></code></pre></details>
|
|
1869
|
+
<!-- Variantes: --outline · --ghost · --accent | Tamaños: --sm (36) · default (44) · --lg (52) --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1436
1870
|
</section>
|
|
1437
1871
|
|
|
1438
1872
|
<!-- ================= SPINNER ================= -->
|
|
@@ -1444,8 +1878,8 @@ html { scroll-behavior: smooth; }
|
|
|
1444
1878
|
<span class="spinner" style="width:24px;height:24px;"></span>
|
|
1445
1879
|
<span class="spinner" style="width:32px;height:32px;color:var(--text-muted);"></span>
|
|
1446
1880
|
</div>
|
|
1447
|
-
<
|
|
1448
|
-
<!-- Hereda currentColor; default accent-400 --></code></pre></details>
|
|
1881
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><span class="spinner"></span>
|
|
1882
|
+
<!-- Hereda currentColor; default accent-400 --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1449
1883
|
</section>
|
|
1450
1884
|
|
|
1451
1885
|
<!-- ================= INPUT ================= -->
|
|
@@ -1474,14 +1908,25 @@ html { scroll-behavior: smooth; }
|
|
|
1474
1908
|
<label class="field__label" for="i4">Deshabilitado</label>
|
|
1475
1909
|
<input class="input" id="i4" type="text" value="Solo lectura" disabled>
|
|
1476
1910
|
</div>
|
|
1911
|
+
<div class="field field--required">
|
|
1912
|
+
<label class="field__label" for="i5">Nombre completo</label>
|
|
1913
|
+
<input class="input" id="i5" type="text" placeholder="Tu nombre" required aria-required="true">
|
|
1914
|
+
<span class="field__help">El asterisco es decorativo; lo obligatorio lo marca <span class="code">required</span>.</span>
|
|
1915
|
+
</div>
|
|
1477
1916
|
</div>
|
|
1478
|
-
<
|
|
1917
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="field">
|
|
1479
1918
|
<label class="field__label" for="email">Correo</label>
|
|
1480
1919
|
<input class="input" id="email" type="email" placeholder="tu@correo.com">
|
|
1481
1920
|
<span class="field__help">Texto de ayuda.</span>
|
|
1482
1921
|
</div>
|
|
1922
|
+
<!-- Obligatorio: .field--required en el .field pinta el asterisco (decorativo); -->
|
|
1923
|
+
<!-- el control lleva required + aria-required="true" -->
|
|
1924
|
+
<div class="field field--required">
|
|
1925
|
+
<label class="field__label" for="name">Nombre</label>
|
|
1926
|
+
<input class="input" id="name" type="text" required aria-required="true">
|
|
1927
|
+
</div>
|
|
1483
1928
|
<!-- Estados: .input--error (+ .field__error) · .input--success · disabled -->
|
|
1484
|
-
<!-- Icono leading: envolver en .input-wrap con un svg antes del input --></code></pre></details>
|
|
1929
|
+
<!-- Icono leading: envolver en .input-wrap con un svg antes del input --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1485
1930
|
</section>
|
|
1486
1931
|
|
|
1487
1932
|
<!-- ================= TEXTAREA ================= -->
|
|
@@ -1495,11 +1940,11 @@ html { scroll-behavior: smooth; }
|
|
|
1495
1940
|
<span class="field__count">0 / 280</span>
|
|
1496
1941
|
</div>
|
|
1497
1942
|
</div>
|
|
1498
|
-
<
|
|
1943
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="field">
|
|
1499
1944
|
<label class="field__label" for="desc">Descripción</label>
|
|
1500
1945
|
<textarea class="textarea" id="desc" placeholder="Describe el proyecto…"></textarea>
|
|
1501
1946
|
<span class="field__count">0 / 280</span>
|
|
1502
|
-
</div></code></pre></details>
|
|
1947
|
+
</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1503
1948
|
</section>
|
|
1504
1949
|
|
|
1505
1950
|
<!-- ================= SELECT ================= -->
|
|
@@ -1517,9 +1962,9 @@ html { scroll-behavior: smooth; }
|
|
|
1517
1962
|
</div>
|
|
1518
1963
|
</div>
|
|
1519
1964
|
</div>
|
|
1520
|
-
<
|
|
1965
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="select-wrap">
|
|
1521
1966
|
<select class="select"><option>Opción</option></select>
|
|
1522
|
-
</div></code></pre></details>
|
|
1967
|
+
</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1523
1968
|
</section>
|
|
1524
1969
|
|
|
1525
1970
|
<!-- ================= SEGMENTEDCONTROL ================= -->
|
|
@@ -1538,11 +1983,11 @@ html { scroll-behavior: smooth; }
|
|
|
1538
1983
|
<input type="radio" name="seg-per" id="seg-12m"><label for="seg-12m">12 m</label>
|
|
1539
1984
|
</div>
|
|
1540
1985
|
</div>
|
|
1541
|
-
<
|
|
1986
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="segmented" role="radiogroup" aria-label="Vista">
|
|
1542
1987
|
<input type="radio" name="vista" id="lista" checked><label for="lista">Lista</label>
|
|
1543
1988
|
<input type="radio" name="vista" id="tablero"><label for="tablero">Tablero</label>
|
|
1544
1989
|
</div>
|
|
1545
|
-
<!-- Compacto: añadir segmented--sm --></code></pre></details>
|
|
1990
|
+
<!-- Compacto: añadir segmented--sm --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1546
1991
|
</section>
|
|
1547
1992
|
|
|
1548
1993
|
<!-- ================= SLIDER ================= -->
|
|
@@ -1559,12 +2004,12 @@ html { scroll-behavior: smooth; }
|
|
|
1559
2004
|
</div>
|
|
1560
2005
|
<input class="slider js-slider" type="range" min="0" max="100" value="70" disabled aria-label="Slider deshabilitado">
|
|
1561
2006
|
</div>
|
|
1562
|
-
<
|
|
2007
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="slider-row">
|
|
1563
2008
|
<span class="slider-label">Límite de gasto</span>
|
|
1564
2009
|
<span class="slider-value">$ 350</span>
|
|
1565
2010
|
</div>
|
|
1566
2011
|
<input class="slider" type="range" min="0" max="1000" value="350">
|
|
1567
|
-
<!-- JS: --slider-fill = ((v-min)/(max-min))*100 + '%' --></code></pre></details>
|
|
2012
|
+
<!-- JS: --slider-fill = ((v-min)/(max-min))*100 + '%' --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1568
2013
|
</section>
|
|
1569
2014
|
|
|
1570
2015
|
<!-- ================= CHECKBOX Y RADIO ================= -->
|
|
@@ -1573,11 +2018,14 @@ html { scroll-behavior: smooth; }
|
|
|
1573
2018
|
<div class="ds-demo" style="gap:32px;">
|
|
1574
2019
|
<label class="check-row"><input type="checkbox" class="checkbox" checked> Notificarme por correo</label>
|
|
1575
2020
|
<label class="check-row"><input type="checkbox" class="checkbox"> Recordar sesión</label>
|
|
2021
|
+
<label class="check-row"><input type="checkbox" class="checkbox" id="cbIndeterminate"> Seleccionar todo <span class="text-muted">(indeterminate)</span></label>
|
|
1576
2022
|
<label class="check-row"><input type="radio" name="r1" class="radio" checked> Mensual</label>
|
|
1577
2023
|
<label class="check-row"><input type="radio" name="r1" class="radio"> Anual</label>
|
|
1578
2024
|
</div>
|
|
1579
|
-
<
|
|
1580
|
-
<label class="check-row"><input type="radio" name="g" class="radio"> Opción</label>
|
|
2025
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><label class="check-row"><input type="checkbox" class="checkbox"> Etiqueta</label>
|
|
2026
|
+
<label class="check-row"><input type="radio" name="g" class="radio"> Opción</label>
|
|
2027
|
+
<!-- Indeterminate = tercer estado (guion horizontal). Solo por JS: -->
|
|
2028
|
+
<!-- document.querySelector('.checkbox').indeterminate = true; --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1581
2029
|
</section>
|
|
1582
2030
|
|
|
1583
2031
|
<!-- ================= SWITCH ================= -->
|
|
@@ -1586,7 +2034,7 @@ html { scroll-behavior: smooth; }
|
|
|
1586
2034
|
<div class="ds-demo" style="gap:32px;">
|
|
1587
2035
|
<label class="check-row"><input type="checkbox" class="switch" checked> Modo compacto</label>
|
|
1588
2036
|
</div>
|
|
1589
|
-
<
|
|
2037
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><label class="check-row"><input type="checkbox" class="switch"> Activar</label></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1590
2038
|
</section>
|
|
1591
2039
|
|
|
1592
2040
|
<!-- ================= BADGES ================= -->
|
|
@@ -1600,8 +2048,8 @@ html { scroll-behavior: smooth; }
|
|
|
1600
2048
|
<span class="tag">diseño <button class="tag__x" aria-label="Quitar etiqueta diseño">×</button></span>
|
|
1601
2049
|
<span class="tag">frontend <button class="tag__x" aria-label="Quitar etiqueta frontend">×</button></span>
|
|
1602
2050
|
</div>
|
|
1603
|
-
<
|
|
1604
|
-
<span class="tag">diseño <button class="tag__x" aria-label="Quitar">×</button></span></code></pre></details>
|
|
2051
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><span class="badge badge--success">Activo</span>
|
|
2052
|
+
<span class="tag">diseño <button class="tag__x" aria-label="Quitar">×</button></span></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1605
2053
|
</section>
|
|
1606
2054
|
|
|
1607
2055
|
<!-- ================= AVATAR ================= -->
|
|
@@ -1615,9 +2063,28 @@ html { scroll-behavior: smooth; }
|
|
|
1615
2063
|
<span class="avatar avatar--lg">hs<span class="avatar__dot avatar__dot--busy"></span></span>
|
|
1616
2064
|
<span class="avatar avatar--xl">hs<span class="avatar__dot avatar__dot--away"></span></span>
|
|
1617
2065
|
</div>
|
|
1618
|
-
<
|
|
2066
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><span class="avatar">HS<span class="avatar__dot avatar__dot--online"></span></span>
|
|
1619
2067
|
<!-- Tamaños: --xs 24 · --sm 32 · default 40 · --lg 48 · --xl 64 -->
|
|
1620
|
-
<!-- Con imagen: <span class="avatar"><img src="…" alt="Nombre"></span> --></code></pre></details>
|
|
2068
|
+
<!-- Con imagen: <span class="avatar"><img src="…" alt="Nombre"></span> --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2069
|
+
|
|
2070
|
+
<h3 style="margin-top:var(--space-8);">AvatarGroup</h3>
|
|
2071
|
+
<p>Avatares solapados 8px con borde <span class="code">2px</span> del color del fondo; el chip <span class="code">.avatar-group__more</span> resume los ocultos.</p>
|
|
2072
|
+
<div class="ds-demo">
|
|
2073
|
+
<div class="avatar-group" role="group" aria-label="Equipo del proyecto: 4 personas y 3 más">
|
|
2074
|
+
<span class="avatar">hs</span>
|
|
2075
|
+
<span class="avatar">ml</span>
|
|
2076
|
+
<span class="avatar">ar</span>
|
|
2077
|
+
<span class="avatar">jt</span>
|
|
2078
|
+
<span class="avatar avatar-group__more">+3</span>
|
|
2079
|
+
</div>
|
|
2080
|
+
</div>
|
|
2081
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="avatar-group" role="group" aria-label="Equipo del proyecto">
|
|
2082
|
+
<span class="avatar">HS</span>
|
|
2083
|
+
<span class="avatar">ML</span>
|
|
2084
|
+
<span class="avatar">AR</span>
|
|
2085
|
+
<span class="avatar avatar-group__more">+3</span>
|
|
2086
|
+
</div>
|
|
2087
|
+
<!-- Solapamiento -8px + borde del color del fondo (--surface-1 / --bg). --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1621
2088
|
</section>
|
|
1622
2089
|
|
|
1623
2090
|
<!-- ================= DIVIDER ================= -->
|
|
@@ -1631,9 +2098,9 @@ html { scroll-behavior: smooth; }
|
|
|
1631
2098
|
<span>Filtros</span><span class="divider--vertical" role="separator"></span><span>Ordenar</span><span class="divider--vertical" role="separator"></span><span>Exportar</span>
|
|
1632
2099
|
</div>
|
|
1633
2100
|
</div>
|
|
1634
|
-
<
|
|
2101
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><hr class="divider">
|
|
1635
2102
|
<div class="divider--label" role="separator">o continúa con</div>
|
|
1636
|
-
<span class="divider--vertical" role="separator"></span></code></pre></details>
|
|
2103
|
+
<span class="divider--vertical" role="separator"></span></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1637
2104
|
</section>
|
|
1638
2105
|
|
|
1639
2106
|
<!-- ================= ACCORDION ================= -->
|
|
@@ -1651,11 +2118,11 @@ html { scroll-behavior: smooth; }
|
|
|
1651
2118
|
</div>
|
|
1652
2119
|
<div class="accordion__item">
|
|
1653
2120
|
<button class="accordion__header" aria-expanded="false">¿Por qué el CTA lleva texto oscuro?<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg></button>
|
|
1654
|
-
<div class="accordion__panel"><div class="accordion__inner"><div class="accordion__content">Negro nocturno sobre celeste eléctrico da
|
|
2121
|
+
<div class="accordion__panel"><div class="accordion__inner"><div class="accordion__content">Negro nocturno sobre celeste eléctrico da 9.50:1 de contraste; blanco sobre celeste no pasa AA.</div></div></div>
|
|
1655
2122
|
</div>
|
|
1656
2123
|
</div>
|
|
1657
2124
|
</div>
|
|
1658
|
-
<
|
|
2125
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="accordion">
|
|
1659
2126
|
<div class="accordion__item">
|
|
1660
2127
|
<button class="accordion__header" aria-expanded="false">
|
|
1661
2128
|
Pregunta <svg>chevron</svg>
|
|
@@ -1665,7 +2132,7 @@ html { scroll-behavior: smooth; }
|
|
|
1665
2132
|
</div></div>
|
|
1666
2133
|
</div>
|
|
1667
2134
|
</div>
|
|
1668
|
-
<!-- JS: alternar aria-expanded en el header --></code></pre></details>
|
|
2135
|
+
<!-- JS: alternar aria-expanded en el header --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1669
2136
|
</section>
|
|
1670
2137
|
|
|
1671
2138
|
<!-- ================= TOOLTIP ================= -->
|
|
@@ -1682,10 +2149,10 @@ html { scroll-behavior: smooth; }
|
|
|
1682
2149
|
<span class="tooltip" role="tooltip">Ajustes</span>
|
|
1683
2150
|
</span>
|
|
1684
2151
|
</div>
|
|
1685
|
-
<
|
|
2152
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><span class="has-tooltip">
|
|
1686
2153
|
<button class="btn btn--outline">Pasa el cursor</button>
|
|
1687
2154
|
<span class="tooltip" role="tooltip">Exportar como CSV</span>
|
|
1688
|
-
</span></code></pre></details>
|
|
2155
|
+
</span></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1689
2156
|
</section>
|
|
1690
2157
|
|
|
1691
2158
|
<!-- ================= ALERT ================= -->
|
|
@@ -1726,7 +2193,7 @@ html { scroll-behavior: smooth; }
|
|
|
1726
2193
|
<button class="alert__close js-alert-close" aria-label="Cerrar aviso"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
1727
2194
|
</div>
|
|
1728
2195
|
</div>
|
|
1729
|
-
<
|
|
2196
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="alert alert--warning" role="status">
|
|
1730
2197
|
<svg>…icono…</svg>
|
|
1731
2198
|
<div>
|
|
1732
2199
|
<p class="alert__title">Tu plan expira en 5 días</p>
|
|
@@ -1734,7 +2201,7 @@ html { scroll-behavior: smooth; }
|
|
|
1734
2201
|
</div>
|
|
1735
2202
|
<button class="alert__close" aria-label="Cerrar aviso">…x…</button>
|
|
1736
2203
|
</div>
|
|
1737
|
-
<!-- Variantes: --info · --success · --warning · --danger --></code></pre></details>
|
|
2204
|
+
<!-- Variantes: --info · --success · --warning · --danger --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1738
2205
|
</section>
|
|
1739
2206
|
|
|
1740
2207
|
<!-- ================= TOAST ================= -->
|
|
@@ -1747,7 +2214,7 @@ html { scroll-behavior: smooth; }
|
|
|
1747
2214
|
<button class="btn btn--outline" data-toast="warning">Toast de aviso</button>
|
|
1748
2215
|
<button class="btn btn--outline" data-toast="danger">Toast de error</button>
|
|
1749
2216
|
</div>
|
|
1750
|
-
<
|
|
2217
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="toast-stack" role="status" aria-live="polite">
|
|
1751
2218
|
<div class="toast toast--success">
|
|
1752
2219
|
<svg>…</svg>
|
|
1753
2220
|
<div>
|
|
@@ -1755,7 +2222,7 @@ html { scroll-behavior: smooth; }
|
|
|
1755
2222
|
<div class="toast__body">El proyecto se actualizó correctamente.</div>
|
|
1756
2223
|
</div>
|
|
1757
2224
|
</div>
|
|
1758
|
-
</div></code></pre></details>
|
|
2225
|
+
</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1759
2226
|
</section>
|
|
1760
2227
|
|
|
1761
2228
|
<!-- ================= PROGRESS ================= -->
|
|
@@ -1765,14 +2232,14 @@ html { scroll-behavior: smooth; }
|
|
|
1765
2232
|
<div class="ds-demo ds-demo--col" style="gap:24px;">
|
|
1766
2233
|
<div>
|
|
1767
2234
|
<div class="progress__head"><span class="progress__label">Subiendo archivos…</span><span class="progress__value" id="pgVal">64 %</span></div>
|
|
1768
|
-
<div class="progress" role="progressbar" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100"><div class="progress__bar" id="pgBar" style="width:64%;"></div></div>
|
|
2235
|
+
<div class="progress" role="progressbar" aria-label="Subiendo archivos" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100"><div class="progress__bar" id="pgBar" style="width:64%;"></div></div>
|
|
1769
2236
|
</div>
|
|
1770
2237
|
<div>
|
|
1771
2238
|
<div class="progress__head"><span class="progress__label">Indeterminado</span></div>
|
|
1772
|
-
<div class="progress progress--indeterminate" role="progressbar"><div class="progress__bar"></div></div>
|
|
2239
|
+
<div class="progress progress--indeterminate" role="progressbar" aria-label="Indeterminado"><div class="progress__bar"></div></div>
|
|
1773
2240
|
</div>
|
|
1774
2241
|
<div style="display:flex;align-items:center;gap:24px;">
|
|
1775
|
-
<div class="progress-ring" id="pgRing" data-value="64" role="progressbar" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100">
|
|
2242
|
+
<div class="progress-ring" id="pgRing" data-value="64" role="progressbar" aria-label="Progreso circular" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100">
|
|
1776
2243
|
<svg width="56" height="56" viewBox="0 0 56 56" aria-hidden="true"><circle class="progress-ring__track" cx="28" cy="28" r="24" stroke-width="5"/><circle class="progress-ring__bar" id="pgRingBar" cx="28" cy="28" r="24" stroke-width="5"/></svg>
|
|
1777
2244
|
<span class="progress-ring__value" id="pgRingVal">64%</span>
|
|
1778
2245
|
</div>
|
|
@@ -1780,7 +2247,7 @@ html { scroll-behavior: smooth; }
|
|
|
1780
2247
|
</div>
|
|
1781
2248
|
<button class="btn btn--outline btn--sm" id="pgRandom" style="align-self:flex-start;">Cambiar progreso</button>
|
|
1782
2249
|
</div>
|
|
1783
|
-
<
|
|
2250
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="progress__head">
|
|
1784
2251
|
<span class="progress__label">Subiendo…</span>
|
|
1785
2252
|
<span class="progress__value">64 %</span>
|
|
1786
2253
|
</div>
|
|
@@ -1796,30 +2263,36 @@ html { scroll-behavior: smooth; }
|
|
|
1796
2263
|
</svg>
|
|
1797
2264
|
<span class="progress-ring__value">64%</span>
|
|
1798
2265
|
</div>
|
|
1799
|
-
<!-- JS: stroke-dasharray = 2πr; stroke-dashoffset = c * (1 - value/100) --></code></pre></details>
|
|
2266
|
+
<!-- JS: stroke-dasharray = 2πr; stroke-dashoffset = c * (1 - value/100) --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1800
2267
|
</section>
|
|
1801
2268
|
|
|
1802
2269
|
<!-- ================= MODAL ================= -->
|
|
1803
2270
|
<section id="comp-modal">
|
|
1804
2271
|
<h2>Modal</h2>
|
|
1805
|
-
<p>Overlay con blur 4px; se cierra con Esc o clic fuera (excepto flujos destructivos)
|
|
2272
|
+
<p>Overlay con blur 4px; se cierra con Esc o clic fuera (excepto flujos destructivos con <span class="code">data-modal-persistent</span>). Al abrir, el foco entra al panel y queda atrapado (Tab/Shift+Tab ciclan dentro); al cerrar, el foco vuelve al botón que lo abrió.</p>
|
|
1806
2273
|
<div class="ds-demo">
|
|
1807
2274
|
<button class="btn btn--primary" id="openModal">Abrir modal</button>
|
|
1808
2275
|
<button class="btn btn--outline" id="openModalLg">Abrir formulario</button>
|
|
1809
2276
|
</div>
|
|
1810
|
-
<
|
|
1811
|
-
<
|
|
2277
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="modal-overlay">
|
|
2278
|
+
<!-- aria-modal, aria-labelledby (título) y aria-describedby (descripción, si la hay);
|
|
2279
|
+
tabindex="-1" permite mover el foco al panel al abrir -->
|
|
2280
|
+
<div class="modal" role="dialog" aria-modal="true"
|
|
2281
|
+
aria-labelledby="mTitle" aria-describedby="mDesc" tabindex="-1">
|
|
1812
2282
|
<div class="modal__header">
|
|
1813
2283
|
<h4 class="modal__title" id="mTitle">Título</h4>
|
|
1814
|
-
<button class="icon-btn icon-btn--ghost" aria-label="Cerrar">×</button>
|
|
2284
|
+
<button class="icon-btn icon-btn--ghost" data-close-modal aria-label="Cerrar">×</button>
|
|
1815
2285
|
</div>
|
|
1816
|
-
<p>Contenido…</p>
|
|
2286
|
+
<p id="mDesc">Contenido…</p>
|
|
1817
2287
|
<div class="modal__footer">
|
|
1818
|
-
<button class="btn btn--outline">Cancelar</button>
|
|
1819
|
-
<button class="btn btn--primary">Confirmar</button>
|
|
2288
|
+
<button class="btn btn--outline" data-close-modal>Cancelar</button>
|
|
2289
|
+
<button class="btn btn--primary" data-close-modal>Confirmar</button>
|
|
1820
2290
|
</div>
|
|
1821
2291
|
</div>
|
|
1822
|
-
</div>
|
|
2292
|
+
</div>
|
|
2293
|
+
<!-- JS: al abrir guarda document.activeElement y enfoca el panel; Tab cicla dentro
|
|
2294
|
+
(focus-trap); Esc / clic en overlay / data-close-modal cierran y devuelven el foco.
|
|
2295
|
+
Destructivo: añade data-modal-persistent al .modal-overlay. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1823
2296
|
</section>
|
|
1824
2297
|
|
|
1825
2298
|
<!-- ================= MENU ================= -->
|
|
@@ -1836,32 +2309,82 @@ html { scroll-behavior: smooth; }
|
|
|
1836
2309
|
</div>
|
|
1837
2310
|
</div>
|
|
1838
2311
|
</div>
|
|
1839
|
-
<
|
|
2312
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="menu">
|
|
1840
2313
|
<button class="menu__item">Duplicar</button>
|
|
1841
2314
|
<button class="menu__item is-selected">Seleccionado</button>
|
|
1842
|
-
</div></code></pre></details>
|
|
2315
|
+
</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1843
2316
|
</section>
|
|
1844
2317
|
|
|
1845
2318
|
<!-- ================= TABLA ================= -->
|
|
1846
2319
|
<section id="comp-tabla">
|
|
1847
2320
|
<h2>Tabla</h2>
|
|
1848
|
-
<
|
|
1849
|
-
|
|
1850
|
-
|
|
2321
|
+
<p>Zebra (<span class="code">.table--zebra</span>) + encabezado fijo (<span class="code">.table--sticky</span>, haz scroll dentro del recuadro) + columna ordenable (clic en «Proyecto» o «Ingresos») + columna de selección. La fila marcada usa <span class="code">.row--selected</span>; el checkbox maestro está en estado indeterminate.</p>
|
|
2322
|
+
<div class="ds-demo ds-demo--col" style="padding:0;max-height:264px;overflow:auto;">
|
|
2323
|
+
<table class="table table--zebra table--sticky" id="demoSortTable">
|
|
2324
|
+
<thead>
|
|
2325
|
+
<tr>
|
|
2326
|
+
<th scope="col" class="col-select"><input type="checkbox" class="checkbox" id="tblSelectAll" aria-label="Seleccionar todas las filas"></th>
|
|
2327
|
+
<th scope="col" aria-sort="ascending"><button class="th-sort" type="button">Proyecto <svg class="sort-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m18 15-6-6-6 6"/></svg></button></th>
|
|
2328
|
+
<th scope="col">Estado</th>
|
|
2329
|
+
<th scope="col" class="num" aria-sort="none"><button class="th-sort" type="button">Ingresos <svg class="sort-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m18 15-6-6-6 6"/></svg></button></th>
|
|
2330
|
+
<th scope="col" class="num">Cambio</th>
|
|
2331
|
+
</tr>
|
|
2332
|
+
</thead>
|
|
1851
2333
|
<tbody>
|
|
1852
|
-
<tr
|
|
1853
|
-
|
|
1854
|
-
|
|
2334
|
+
<tr>
|
|
2335
|
+
<td class="col-select"><input type="checkbox" class="checkbox" aria-label="Seleccionar API de pagos"></td>
|
|
2336
|
+
<td style="color:var(--text-primary);">API de pagos</td>
|
|
2337
|
+
<td><span class="badge badge--info"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4m0-4h.01"/></svg>Beta</span></td>
|
|
2338
|
+
<td class="num" data-sort="31990">$31 990</td>
|
|
2339
|
+
<td class="num" style="color:var(--success);">+2,4 %</td>
|
|
2340
|
+
</tr>
|
|
2341
|
+
<tr class="row--selected">
|
|
2342
|
+
<td class="col-select"><input type="checkbox" class="checkbox" checked aria-label="Seleccionar App móvil"></td>
|
|
2343
|
+
<td style="color:var(--text-primary);">App móvil</td>
|
|
2344
|
+
<td><span class="badge badge--warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.7 18-8-14a2 2 0 0 0-3.5 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.7-3z"/><path d="M12 9v4m0 4h.01"/></svg>Pendiente</span></td>
|
|
2345
|
+
<td class="num" data-sort="12040">$12 040</td>
|
|
2346
|
+
<td class="num" style="color:var(--danger);">−1,1 %</td>
|
|
2347
|
+
</tr>
|
|
2348
|
+
<tr>
|
|
2349
|
+
<td class="col-select"><input type="checkbox" class="checkbox" aria-label="Seleccionar Panel analítico"></td>
|
|
2350
|
+
<td style="color:var(--text-primary);">Panel analítico</td>
|
|
2351
|
+
<td><span class="badge badge--success"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>Activo</span></td>
|
|
2352
|
+
<td class="num" data-sort="48210">$48 210</td>
|
|
2353
|
+
<td class="num" style="color:var(--success);">+8,2 %</td>
|
|
2354
|
+
</tr>
|
|
2355
|
+
<tr>
|
|
2356
|
+
<td class="col-select"><input type="checkbox" class="checkbox" aria-label="Seleccionar Portal de socios"></td>
|
|
2357
|
+
<td style="color:var(--text-primary);">Portal de socios</td>
|
|
2358
|
+
<td><span class="badge badge--success"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>Activo</span></td>
|
|
2359
|
+
<td class="num" data-sort="27500">$27 500</td>
|
|
2360
|
+
<td class="num" style="color:var(--success);">+5,0 %</td>
|
|
2361
|
+
</tr>
|
|
2362
|
+
<tr>
|
|
2363
|
+
<td class="col-select"><input type="checkbox" class="checkbox" aria-label="Seleccionar Reportes"></td>
|
|
2364
|
+
<td style="color:var(--text-primary);">Reportes</td>
|
|
2365
|
+
<td><span class="badge badge--info"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4m0-4h.01"/></svg>Beta</span></td>
|
|
2366
|
+
<td class="num" data-sort="9800">$9 800</td>
|
|
2367
|
+
<td class="num" style="color:var(--danger);">−0,4 %</td>
|
|
2368
|
+
</tr>
|
|
1855
2369
|
</tbody>
|
|
1856
2370
|
</table>
|
|
1857
2371
|
</div>
|
|
1858
|
-
<
|
|
1859
|
-
<thead><tr>
|
|
2372
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><table class="table table--zebra table--sticky">
|
|
2373
|
+
<thead><tr>
|
|
2374
|
+
<th scope="col" class="col-select">
|
|
2375
|
+
<input type="checkbox" class="checkbox" aria-label="Seleccionar todas"></th>
|
|
2376
|
+
<th scope="col" aria-sort="ascending">
|
|
2377
|
+
<button class="th-sort" type="button">Proyecto
|
|
2378
|
+
<svg class="sort-icon" …><path d="m18 15-6-6-6 6"/></svg>
|
|
2379
|
+
</button></th>
|
|
2380
|
+
<th scope="col" class="num">Ingresos</th>
|
|
2381
|
+
</tr></thead>
|
|
1860
2382
|
<tbody>
|
|
1861
|
-
<tr><td
|
|
1862
|
-
<tr class="
|
|
2383
|
+
<tr><td class="col-select"><input type="checkbox" class="checkbox" aria-label="Seleccionar API de pagos"></td>…</tr>
|
|
2384
|
+
<tr class="row--selected">…</tr>
|
|
1863
2385
|
</tbody>
|
|
1864
|
-
</table>
|
|
2386
|
+
</table>
|
|
2387
|
+
<!-- El icono se atenúa si aria-sort no está fijado; JS reordena y alterna aria-sort. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1865
2388
|
</section>
|
|
1866
2389
|
|
|
1867
2390
|
<!-- ================= STAT ================= -->
|
|
@@ -1885,11 +2408,30 @@ html { scroll-behavior: smooth; }
|
|
|
1885
2408
|
<span class="stat__delta stat__delta--neutral">— sin cambios</span>
|
|
1886
2409
|
</div>
|
|
1887
2410
|
</div>
|
|
1888
|
-
<
|
|
2411
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="stat">
|
|
1889
2412
|
<span class="stat__label">Ingresos del mes</span>
|
|
1890
2413
|
<span class="stat__value">$128 450</span>
|
|
1891
2414
|
<span class="stat__delta stat__delta--up">▲ +12,4 %</span>
|
|
1892
|
-
</div></code></pre></details>
|
|
2415
|
+
</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2416
|
+
<h3 style="margin-top:32px;">Chip de métrica</h3>
|
|
2417
|
+
<p>Pastilla de una línea con rótulo y valor en <span class="code">--link</span> · fila con <span class="code">gap</span> que salta de línea en móvil · el chip interactivo es un <span class="code"><button></span> de 44 px con nombre accesible completo.</p>
|
|
2418
|
+
<div class="ds-demo">
|
|
2419
|
+
<div class="metric-chips">
|
|
2420
|
+
<span class="metric-chip"><span class="metric-chip__label">Latencia</span> <span class="metric-chip__value">42 ms</span></span>
|
|
2421
|
+
<span class="metric-chip"><span class="metric-chip__label">Errores</span> <span class="metric-chip__value">0,3 %</span></span>
|
|
2422
|
+
<button class="metric-chip" type="button" id="dsWaterChip" aria-label="Agua: 3 de 8 vasos. Sumar un vaso"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg><span class="metric-chip__label">Agua</span> <span class="metric-chip__value">3 de 8</span></button>
|
|
2423
|
+
</div>
|
|
2424
|
+
</div>
|
|
2425
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="metric-chips">
|
|
2426
|
+
<span class="metric-chip">
|
|
2427
|
+
<span class="metric-chip__label">Latencia</span> <span class="metric-chip__value">42 ms</span>
|
|
2428
|
+
</span>
|
|
2429
|
+
<button class="metric-chip" type="button" aria-label="Agua: 3 de 8 vasos. Sumar un vaso">
|
|
2430
|
+
<svg aria-hidden="true">…plus…</svg>
|
|
2431
|
+
<span class="metric-chip__label">Agua</span> <span class="metric-chip__value">3 de 8</span>
|
|
2432
|
+
</button>
|
|
2433
|
+
</div>
|
|
2434
|
+
<!-- El aria-label se actualiza con el valor; empieza por el texto visible --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1893
2435
|
</section>
|
|
1894
2436
|
|
|
1895
2437
|
<!-- ================= PAGINATION ================= -->
|
|
@@ -1899,14 +2441,14 @@ html { scroll-behavior: smooth; }
|
|
|
1899
2441
|
<div class="ds-demo">
|
|
1900
2442
|
<ul class="pagination" id="paginationDemo" aria-label="Paginación"></ul>
|
|
1901
2443
|
</div>
|
|
1902
|
-
<
|
|
2444
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><ul class="pagination" aria-label="Paginación">
|
|
1903
2445
|
<li><button class="pagination__btn" data-page="prev" aria-label="Página anterior">‹</button></li>
|
|
1904
2446
|
<li><button class="pagination__btn" data-page="1">1</button></li>
|
|
1905
2447
|
<li><span class="pagination__ellipsis">…</span></li>
|
|
1906
2448
|
<li><button class="pagination__btn is-active" data-page="5" aria-current="page">5</button></li>
|
|
1907
2449
|
<li><button class="pagination__btn" data-page="6">6</button></li>
|
|
1908
2450
|
<li><button class="pagination__btn" data-page="next" aria-label="Página siguiente">›</button></li>
|
|
1909
|
-
</ul></code></pre></details>
|
|
2451
|
+
</ul></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1910
2452
|
</section>
|
|
1911
2453
|
|
|
1912
2454
|
<!-- ================= NAVEGACIÓN ================= -->
|
|
@@ -1931,6 +2473,69 @@ html { scroll-behavior: smooth; }
|
|
|
1931
2473
|
<a class="sidebar__item" href="#comp-navegacion"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>Proyectos</a>
|
|
1932
2474
|
</div>
|
|
1933
2475
|
</div>
|
|
2476
|
+
<h3 style="margin-top:32px;">Sidebar contraíble</h3>
|
|
2477
|
+
<p>Expandido 260 px · riel de iconos de 64 px con grupos separados por una línea · en el riel, el nombre aparece como tooltip al pasar el cursor o al enfocar con teclado · el ancho cambia sin transición y solo los rótulos se funden · por debajo de 768 px lo sustituye un Drawer <span class="code">--left</span>.</p>
|
|
2478
|
+
<div class="ds-demo ds-app-demo">
|
|
2479
|
+
<nav class="sidebar sidebar--collapsible" id="demoAppNav" aria-label="Navegación de ejemplo">
|
|
2480
|
+
<div class="sidebar__group" role="group" aria-labelledby="demoAppNavG1">
|
|
2481
|
+
<span class="sidebar__group-label" id="demoAppNavG1">Monitoreo</span>
|
|
2482
|
+
<a class="sidebar__item is-active" href="#comp-navegacion" aria-current="page"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></svg><span class="sidebar__label">Inicio</span></a>
|
|
2483
|
+
<a class="sidebar__item" href="#comp-navegacion"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v18h18"/><path d="M7 15v3m5-8v8m5-13v13"/></svg><span class="sidebar__label">Analítica</span></a>
|
|
2484
|
+
<a class="sidebar__item" href="#comp-navegacion"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg><span class="sidebar__label">Actividad</span></a>
|
|
2485
|
+
</div>
|
|
2486
|
+
<div class="sidebar__group" role="group" aria-labelledby="demoAppNavG2">
|
|
2487
|
+
<span class="sidebar__group-label" id="demoAppNavG2">Gestión</span>
|
|
2488
|
+
<a class="sidebar__item" href="#comp-navegacion"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg><span class="sidebar__label">Proyectos</span></a>
|
|
2489
|
+
<a class="sidebar__item" href="#comp-navegacion"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg><span class="sidebar__label">Equipo</span></a>
|
|
2490
|
+
<a class="sidebar__item" href="#comp-navegacion"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg><span class="sidebar__label">Alertas</span></a>
|
|
2491
|
+
</div>
|
|
2492
|
+
<button class="sidebar__item sidebar__toggle" type="button" aria-expanded="true" aria-controls="demoAppNav"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg><span class="sidebar__label">Contraer</span></button>
|
|
2493
|
+
</nav>
|
|
2494
|
+
<div class="ds-app-demo__main">
|
|
2495
|
+
<button class="icon-btn icon-btn--outline sidebar-trigger" type="button" id="openNavDrawer" aria-label="Abrir navegación" aria-expanded="false" aria-controls="demoNavDrawer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg></button>
|
|
2496
|
+
<h4 style="margin:0;">Inicio</h4>
|
|
2497
|
+
<p class="text-muted" style="margin:0;font-size:var(--text-sm);">El contenido ocupa el ancho que deja el sidebar: la rejilla <span class="code">auto 1fr</span> sigue al riel al instante, sin animar el ancho.</p>
|
|
2498
|
+
</div>
|
|
2499
|
+
</div>
|
|
2500
|
+
<div class="drawer drawer--left" id="demoNavDrawer" hidden>
|
|
2501
|
+
<div class="drawer__overlay" data-close-drawer></div>
|
|
2502
|
+
<aside class="drawer__panel" role="dialog" aria-modal="true" aria-labelledby="navDrawerTitle" tabindex="-1">
|
|
2503
|
+
<div class="drawer__header">
|
|
2504
|
+
<h3 class="drawer__title" id="navDrawerTitle">Navegación</h3>
|
|
2505
|
+
<button class="icon-btn icon-btn--ghost" data-close-drawer aria-label="Cerrar navegación"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
2506
|
+
</div>
|
|
2507
|
+
<nav class="drawer__body" aria-label="Navegación de ejemplo">
|
|
2508
|
+
<div class="sidebar__group" role="group" aria-labelledby="demoNavDrawerG1">
|
|
2509
|
+
<span class="sidebar__group-label" id="demoNavDrawerG1">Monitoreo</span>
|
|
2510
|
+
<a class="sidebar__item is-active" href="#comp-navegacion" aria-current="page" data-close-drawer><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></svg><span class="sidebar__label">Inicio</span></a>
|
|
2511
|
+
<a class="sidebar__item" href="#comp-navegacion" data-close-drawer><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v18h18"/><path d="M7 15v3m5-8v8m5-13v13"/></svg><span class="sidebar__label">Analítica</span></a>
|
|
2512
|
+
<a class="sidebar__item" href="#comp-navegacion" data-close-drawer><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg><span class="sidebar__label">Actividad</span></a>
|
|
2513
|
+
</div>
|
|
2514
|
+
<div class="sidebar__group" role="group" aria-labelledby="demoNavDrawerG2">
|
|
2515
|
+
<span class="sidebar__group-label" id="demoNavDrawerG2">Gestión</span>
|
|
2516
|
+
<a class="sidebar__item" href="#comp-navegacion" data-close-drawer><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg><span class="sidebar__label">Proyectos</span></a>
|
|
2517
|
+
<a class="sidebar__item" href="#comp-navegacion" data-close-drawer><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg><span class="sidebar__label">Equipo</span></a>
|
|
2518
|
+
<a class="sidebar__item" href="#comp-navegacion" data-close-drawer><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg><span class="sidebar__label">Alertas</span></a>
|
|
2519
|
+
</div>
|
|
2520
|
+
</nav>
|
|
2521
|
+
</aside>
|
|
2522
|
+
</div>
|
|
2523
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><nav class="sidebar sidebar--collapsible" id="appNav" aria-label="Principal">
|
|
2524
|
+
<div class="sidebar__group" role="group" aria-labelledby="navMonitor">
|
|
2525
|
+
<span class="sidebar__group-label" id="navMonitor">Monitoreo</span>
|
|
2526
|
+
<a class="sidebar__item is-active" href="/" aria-current="page">
|
|
2527
|
+
<svg aria-hidden="true">…</svg><span class="sidebar__label">Inicio</span>
|
|
2528
|
+
</a>
|
|
2529
|
+
</div>
|
|
2530
|
+
<button class="sidebar__item sidebar__toggle" type="button"
|
|
2531
|
+
aria-expanded="true" aria-controls="appNav">
|
|
2532
|
+
<svg aria-hidden="true">…chevron-left…</svg><span class="sidebar__label">Contraer</span>
|
|
2533
|
+
</button>
|
|
2534
|
+
</nav>
|
|
2535
|
+
<!-- Contraído: .is-collapsed en el nav, aria-expanded="false" y texto «Expandir».
|
|
2536
|
+
Por debajo de 768 px: botón ☰ .sidebar-trigger que abre un .drawer.drawer--left con los mismos ítems. -->
|
|
2537
|
+
<button class="icon-btn sidebar-trigger" type="button" aria-label="Abrir navegación"
|
|
2538
|
+
aria-expanded="false" aria-controls="navDrawer">…menu…</button></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1934
2539
|
<h3 style="margin-top:32px;">Tabs y Breadcrumbs</h3>
|
|
1935
2540
|
<div class="ds-demo ds-demo--col">
|
|
1936
2541
|
<div class="tabs" id="demoTabs">
|
|
@@ -1945,10 +2550,10 @@ html { scroll-behavior: smooth; }
|
|
|
1945
2550
|
<li aria-current="page">Panel analítico</li>
|
|
1946
2551
|
</ul>
|
|
1947
2552
|
</div>
|
|
1948
|
-
<
|
|
2553
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="navbar">…<a class="navbar__link is-active">Resumen</a>…</div>
|
|
1949
2554
|
<div class="sidebar"><a class="sidebar__item is-active"><svg>…</svg>Inicio</a></div>
|
|
1950
2555
|
<div class="tabs"><button class="tabs__btn is-active">Resumen</button>…<span class="tabs__indicator"></span></div>
|
|
1951
|
-
<ul class="breadcrumbs"><li><a>Inicio</a></li><li aria-current="page">Actual</li></ul></code></pre></details>
|
|
2556
|
+
<ul class="breadcrumbs"><li><a>Inicio</a></li><li aria-current="page">Actual</li></ul></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1952
2557
|
</section>
|
|
1953
2558
|
|
|
1954
2559
|
<!-- ================= SKELETON ================= -->
|
|
@@ -1969,7 +2574,7 @@ html { scroll-behavior: smooth; }
|
|
|
1969
2574
|
<button class="btn btn--primary btn--sm">Crear proyecto</button>
|
|
1970
2575
|
</div>
|
|
1971
2576
|
</div>
|
|
1972
|
-
<
|
|
2577
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><span class="skeleton" style="height:12px;width:70%"></span>
|
|
1973
2578
|
<span class="skeleton skeleton--circle" style="width:40px;height:40px"></span>
|
|
1974
2579
|
|
|
1975
2580
|
<div class="empty-state">
|
|
@@ -1977,7 +2582,7 @@ html { scroll-behavior: smooth; }
|
|
|
1977
2582
|
<span class="empty-state__title">Aún no hay proyectos</span>
|
|
1978
2583
|
<span class="empty-state__desc">Crea tu primer proyecto…</span>
|
|
1979
2584
|
<button class="btn btn--primary btn--sm">Crear proyecto</button>
|
|
1980
|
-
</div></code></pre></details>
|
|
2585
|
+
</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
1981
2586
|
</section>
|
|
1982
2587
|
|
|
1983
2588
|
<!-- ================= CARD, LINK Y CODE ================= -->
|
|
@@ -1994,17 +2599,380 @@ html { scroll-behavior: smooth; }
|
|
|
1994
2599
|
<p style="margin:0;font-size:var(--text-sm);color:var(--text-muted);">Al pasar el cursor sube de nivel: −2px + superficie más clara.</p>
|
|
1995
2600
|
</div>
|
|
1996
2601
|
</div>
|
|
1997
|
-
<
|
|
2602
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="card">…</div>
|
|
1998
2603
|
<div class="card card--interactive">…</div>
|
|
1999
|
-
<!-- .card--glow: firma de marca — MÁXIMO UNO POR PANTALLA --></code></pre></details>
|
|
2604
|
+
<!-- .card--glow: firma de marca — MÁXIMO UNO POR PANTALLA --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2000
2605
|
<h3 style="margin-top:32px;">Link y Code</h3>
|
|
2001
2606
|
<div class="ds-demo" style="gap:32px;">
|
|
2002
2607
|
<a class="link" href="#comp-card">Link con subrayado animado</a>
|
|
2003
2608
|
<a class="link" href="https://lucide.dev" target="_blank" rel="noopener noreferrer">Link externo<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7M7 7h10v10"/></svg></a>
|
|
2004
2609
|
<span>Acento con <span class="code">var(--accent-400)</span></span>
|
|
2005
2610
|
</div>
|
|
2006
|
-
<
|
|
2007
|
-
<span class="code">var(--accent-400)</span></code></pre></details>
|
|
2611
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><a class="link" href="…">Link con subrayado animado</a>
|
|
2612
|
+
<span class="code">var(--accent-400)</span></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2613
|
+
</section>
|
|
2614
|
+
|
|
2615
|
+
<!-- ================= COMBOBOX / SEARCH ================= -->
|
|
2616
|
+
<section id="comp-combobox">
|
|
2617
|
+
<h2>Combobox y Search</h2>
|
|
2618
|
+
<p>Combobox con autocompletado: filtra en vivo y se navega con ↑/↓, Enter selecciona, Esc cierra. El listado vive sobre <span class="code">--surface-2</span>. Al lado, un campo de búsqueda con botón para limpiar.</p>
|
|
2619
|
+
<div class="ds-demo ds-demo--grid" style="align-items:start;overflow:visible;">
|
|
2620
|
+
<div>
|
|
2621
|
+
<span class="field__label" style="display:block;margin-bottom:6px;">Framework</span>
|
|
2622
|
+
<div class="combobox">
|
|
2623
|
+
<input class="input combobox__input" type="text" id="cbInput"
|
|
2624
|
+
role="combobox" aria-expanded="false" aria-controls="cbList"
|
|
2625
|
+
aria-autocomplete="list" aria-activedescendant=""
|
|
2626
|
+
placeholder="Escribe para filtrar…" autocomplete="off" spellcheck="false">
|
|
2627
|
+
<ul class="combobox__list" id="cbList" role="listbox" aria-label="Frameworks" hidden>
|
|
2628
|
+
<li class="combobox__option" role="option" id="cbo-0" aria-selected="false">Angular<svg class="combobox__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></li>
|
|
2629
|
+
<li class="combobox__option" role="option" id="cbo-1" aria-selected="false">Astro<svg class="combobox__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></li>
|
|
2630
|
+
<li class="combobox__option" role="option" id="cbo-2" aria-selected="false">React<svg class="combobox__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></li>
|
|
2631
|
+
<li class="combobox__option" role="option" id="cbo-3" aria-selected="false">Solid<svg class="combobox__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></li>
|
|
2632
|
+
<li class="combobox__option" role="option" id="cbo-4" aria-selected="false">Svelte<svg class="combobox__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></li>
|
|
2633
|
+
<li class="combobox__option" role="option" id="cbo-5" aria-selected="false">Vue<svg class="combobox__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></li>
|
|
2634
|
+
</ul>
|
|
2635
|
+
</div>
|
|
2636
|
+
</div>
|
|
2637
|
+
<div>
|
|
2638
|
+
<span class="field__label" style="display:block;margin-bottom:6px;">Buscar</span>
|
|
2639
|
+
<div class="search">
|
|
2640
|
+
<svg class="search__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
2641
|
+
<input class="input search__input" type="search" id="searchInput" placeholder="Buscar proyectos…" aria-label="Buscar proyectos" autocomplete="off">
|
|
2642
|
+
<button class="search__clear" type="button" id="searchClear" aria-label="Limpiar" hidden><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
2643
|
+
</div>
|
|
2644
|
+
</div>
|
|
2645
|
+
</div>
|
|
2646
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="combobox">
|
|
2647
|
+
<input class="input combobox__input" role="combobox" aria-expanded="false"
|
|
2648
|
+
aria-controls="lista" aria-autocomplete="list" aria-activedescendant="">
|
|
2649
|
+
<ul class="combobox__list" id="lista" role="listbox" aria-label="Frameworks" hidden>
|
|
2650
|
+
<li class="combobox__option" role="option" id="op-0" aria-selected="false">Angular</li>
|
|
2651
|
+
<!-- .is-active = resaltado por teclado · aria-selected="true" = elegida -->
|
|
2652
|
+
</ul>
|
|
2653
|
+
</div>
|
|
2654
|
+
|
|
2655
|
+
<div class="search">
|
|
2656
|
+
<svg class="search__icon" aria-hidden="true">…lupa…</svg>
|
|
2657
|
+
<input class="input search__input" type="search" aria-label="Buscar">
|
|
2658
|
+
<button class="search__clear" aria-label="Limpiar" hidden>×</button>
|
|
2659
|
+
</div>
|
|
2660
|
+
<!-- JS: filtra en vivo, ↑/↓/Enter/Esc; el clear vacía y devuelve el foco. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2661
|
+
</section>
|
|
2662
|
+
|
|
2663
|
+
<!-- ================= DRAWER ================= -->
|
|
2664
|
+
<section id="comp-drawer">
|
|
2665
|
+
<h2>Drawer</h2>
|
|
2666
|
+
<p>Panel que entra deslizándose desde un borde (derecha por defecto; <span class="code">--left</span> / <span class="code">--bottom</span>). Igual que el modal: <span class="code">role="dialog"</span> + <span class="code">aria-modal</span>, foco atrapado, retorno de foco, y cierre con Esc o clic en el overlay. El panel usa <span class="code">overscroll-behavior: contain</span>.</p>
|
|
2667
|
+
<div class="ds-demo">
|
|
2668
|
+
<button class="btn btn--primary" id="openDrawer">Abrir panel</button>
|
|
2669
|
+
</div>
|
|
2670
|
+
<div class="drawer drawer--right" id="demoDrawer" hidden>
|
|
2671
|
+
<div class="drawer__overlay" data-close-drawer></div>
|
|
2672
|
+
<aside class="drawer__panel" role="dialog" aria-modal="true" aria-labelledby="drawerTitle" tabindex="-1">
|
|
2673
|
+
<div class="drawer__header">
|
|
2674
|
+
<h3 class="drawer__title" id="drawerTitle">Filtros</h3>
|
|
2675
|
+
<button class="icon-btn icon-btn--ghost" data-close-drawer aria-label="Cerrar panel"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
2676
|
+
</div>
|
|
2677
|
+
<div class="drawer__body">
|
|
2678
|
+
<div class="field">
|
|
2679
|
+
<label class="field__label" for="drawerRange">Rango de fechas</label>
|
|
2680
|
+
<div class="select-wrap">
|
|
2681
|
+
<select class="select" id="drawerRange">
|
|
2682
|
+
<option>Últimos 7 días</option>
|
|
2683
|
+
<option>Últimos 30 días</option>
|
|
2684
|
+
<option>Este trimestre</option>
|
|
2685
|
+
</select>
|
|
2686
|
+
</div>
|
|
2687
|
+
</div>
|
|
2688
|
+
<div style="display:flex;flex-direction:column;gap:var(--space-3);">
|
|
2689
|
+
<label class="check-row"><input type="checkbox" class="checkbox" checked> Solo proyectos activos</label>
|
|
2690
|
+
<label class="check-row"><input type="checkbox" class="checkbox"> Incluir archivados</label>
|
|
2691
|
+
</div>
|
|
2692
|
+
</div>
|
|
2693
|
+
<div class="drawer__footer">
|
|
2694
|
+
<button class="btn btn--outline" data-close-drawer>Cancelar</button>
|
|
2695
|
+
<button class="btn btn--primary" data-close-drawer>Aplicar filtros</button>
|
|
2696
|
+
</div>
|
|
2697
|
+
</aside>
|
|
2698
|
+
</div>
|
|
2699
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="drawer drawer--right" hidden>
|
|
2700
|
+
<div class="drawer__overlay" data-close-drawer></div>
|
|
2701
|
+
<aside class="drawer__panel" role="dialog" aria-modal="true"
|
|
2702
|
+
aria-labelledby="dTitle" tabindex="-1">
|
|
2703
|
+
<div class="drawer__header">
|
|
2704
|
+
<h3 class="drawer__title" id="dTitle">Filtros</h3>
|
|
2705
|
+
<button class="icon-btn icon-btn--ghost" data-close-drawer aria-label="Cerrar">×</button>
|
|
2706
|
+
</div>
|
|
2707
|
+
<div class="drawer__body">…</div>
|
|
2708
|
+
<div class="drawer__footer">…</div>
|
|
2709
|
+
</aside>
|
|
2710
|
+
</div>
|
|
2711
|
+
<!-- Variantes: --right (default) · --left · --bottom (sheet).
|
|
2712
|
+
JS: .is-open dispara el slide; Esc / overlay cierran y devuelven el foco. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2713
|
+
</section>
|
|
2714
|
+
|
|
2715
|
+
<!-- ================= POPOVER ================= -->
|
|
2716
|
+
<section id="comp-popover">
|
|
2717
|
+
<h2>Popover</h2>
|
|
2718
|
+
<p>Se abre por <strong>clic</strong> —eso lo distingue del Tooltip, que es hover— y crece desde el <span class="code">transform-origin</span> del disparador. <span class="code">role="dialog"</span>, el foco entra al panel; cierra con Esc o clic fuera. Panel sobre <span class="code">--surface-2</span>.</p>
|
|
2719
|
+
<div class="ds-demo" style="overflow:visible;">
|
|
2720
|
+
<div class="popover">
|
|
2721
|
+
<button class="btn btn--outline popover__trigger" type="button" id="popTrigger" aria-haspopup="dialog" aria-expanded="false" aria-controls="popPanel">
|
|
2722
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="m16 6-4-4-4 4"/><path d="M12 2v13"/></svg>
|
|
2723
|
+
Compartir
|
|
2724
|
+
</button>
|
|
2725
|
+
<div class="popover__panel" role="dialog" aria-label="Compartir proyecto" id="popPanel">
|
|
2726
|
+
<h3 style="margin:0 0 var(--space-2);font-family:var(--font-body);font-size:var(--text-base);font-weight:600;letter-spacing:normal;color:var(--text-heading);">Compartir proyecto</h3>
|
|
2727
|
+
<p style="font-size:var(--text-sm);color:var(--text-muted);margin:0 0 var(--space-3);max-width:none;">Cualquiera con el enlace podrá ver este proyecto en modo lectura.</p>
|
|
2728
|
+
<div style="display:flex;gap:var(--space-2);align-items:center;">
|
|
2729
|
+
<input class="input" type="text" readonly value="higinio.dev/p/8f2a" aria-label="Enlace para compartir">
|
|
2730
|
+
<button class="btn btn--primary btn--sm" type="button">Copiar</button>
|
|
2731
|
+
</div>
|
|
2732
|
+
<a class="link" href="#comp-popover" style="margin-top:var(--space-3);">Ver permisos avanzados</a>
|
|
2733
|
+
</div>
|
|
2734
|
+
</div>
|
|
2735
|
+
</div>
|
|
2736
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="popover">
|
|
2737
|
+
<button class="btn btn--outline popover__trigger" type="button"
|
|
2738
|
+
aria-haspopup="dialog" aria-expanded="false" aria-controls="panel">Compartir</button>
|
|
2739
|
+
<div class="popover__panel" role="dialog" aria-label="Compartir proyecto" id="panel">
|
|
2740
|
+
<p>Contenido de varias líneas…</p>
|
|
2741
|
+
<button class="btn btn--primary btn--sm">Copiar</button>
|
|
2742
|
+
<a class="link" href="…">Ver permisos avanzados</a>
|
|
2743
|
+
</div>
|
|
2744
|
+
</div>
|
|
2745
|
+
<!-- Abre por CLICK (no hover); el foco entra al panel; Esc / clic fuera cierran. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2746
|
+
</section>
|
|
2747
|
+
|
|
2748
|
+
<!-- ================= BUTTON GROUP ================= -->
|
|
2749
|
+
<section id="comp-buttongroup">
|
|
2750
|
+
<h2>ButtonGroup</h2>
|
|
2751
|
+
<p>Botones adyacentes con bordes compartidos: el radio queda solo en los extremos y los bordes internos se colapsan. Reutiliza los <span class="code">.btn</span> existentes. <span class="code">role="group"</span> + <span class="code">aria-label</span>.</p>
|
|
2752
|
+
<div class="ds-demo">
|
|
2753
|
+
<div class="btn-group" role="group" aria-label="Alineación del texto">
|
|
2754
|
+
<button class="btn btn--outline" type="button">Izquierda</button>
|
|
2755
|
+
<button class="btn btn--outline" type="button">Centro</button>
|
|
2756
|
+
<button class="btn btn--outline" type="button">Derecha</button>
|
|
2757
|
+
</div>
|
|
2758
|
+
</div>
|
|
2759
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="btn-group" role="group" aria-label="Alineación del texto">
|
|
2760
|
+
<button class="btn btn--outline">Izquierda</button>
|
|
2761
|
+
<button class="btn btn--outline">Centro</button>
|
|
2762
|
+
<button class="btn btn--outline">Derecha</button>
|
|
2763
|
+
</div>
|
|
2764
|
+
<!-- Radios solo en los extremos; bordes internos colapsados (margin-left:-1px). --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2765
|
+
</section>
|
|
2766
|
+
|
|
2767
|
+
<!-- ================= DATEPICKER ================= -->
|
|
2768
|
+
<section id="comp-datepicker">
|
|
2769
|
+
<h2>DatePicker</h2>
|
|
2770
|
+
<p>Campo de fecha con calendario en popover: el input abre el calendario al hacer clic, eliges un día con el ratón o las flechas y el valor vuelve al campo. El panel reutiliza el look de <span class="code">.popover__panel</span> sobre <span class="code">--surface-2</span>. El <span class="code">.calendar__grid</span> es <span class="code">role="grid"</span> y cada día un <span class="code"><button role="gridcell"></span> con la fecha completa en <span class="code">aria-label</span>.</p>
|
|
2771
|
+
<div class="ds-demo" style="overflow:visible;">
|
|
2772
|
+
<div class="datepicker" id="datepicker">
|
|
2773
|
+
<span class="field__label" id="dpLabel" style="display:block;margin-bottom:6px;">Fecha de entrega</span>
|
|
2774
|
+
<div class="input-wrap" style="max-width:220px;">
|
|
2775
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
|
|
2776
|
+
<input class="input datepicker__input" type="text" id="dpInput" readonly
|
|
2777
|
+
aria-haspopup="dialog" aria-controls="dpCalendar"
|
|
2778
|
+
aria-labelledby="dpLabel" value="15 de julio de 2026">
|
|
2779
|
+
</div>
|
|
2780
|
+
<div class="datepicker__calendar" id="dpCalendar" role="dialog" aria-label="Elegir fecha" hidden>
|
|
2781
|
+
<div class="calendar">
|
|
2782
|
+
<div class="calendar__header">
|
|
2783
|
+
<button class="icon-btn icon-btn--ghost icon-btn--sm" type="button" id="dpPrev" aria-label="Mes anterior"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
|
|
2784
|
+
<span class="calendar__title" id="dpTitle" aria-live="polite">julio 2026</span>
|
|
2785
|
+
<button class="icon-btn icon-btn--ghost icon-btn--sm" type="button" id="dpNext" aria-label="Mes siguiente"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg></button>
|
|
2786
|
+
</div>
|
|
2787
|
+
<div class="calendar__table" role="grid" aria-labelledby="dpTitle">
|
|
2788
|
+
<div class="calendar__weekdays" role="row">
|
|
2789
|
+
<span role="columnheader" aria-label="lunes">L</span>
|
|
2790
|
+
<span role="columnheader" aria-label="martes">M</span>
|
|
2791
|
+
<span role="columnheader" aria-label="miércoles">X</span>
|
|
2792
|
+
<span role="columnheader" aria-label="jueves">J</span>
|
|
2793
|
+
<span role="columnheader" aria-label="viernes">V</span>
|
|
2794
|
+
<span role="columnheader" aria-label="sábado">S</span>
|
|
2795
|
+
<span role="columnheader" aria-label="domingo">D</span>
|
|
2796
|
+
</div>
|
|
2797
|
+
<div class="calendar__grid" id="dpGrid" role="rowgroup"></div>
|
|
2798
|
+
</div>
|
|
2799
|
+
</div>
|
|
2800
|
+
</div>
|
|
2801
|
+
</div>
|
|
2802
|
+
</div>
|
|
2803
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="datepicker">
|
|
2804
|
+
<div class="input-wrap">
|
|
2805
|
+
<svg aria-hidden="true">…calendario…</svg>
|
|
2806
|
+
<input class="input datepicker__input" readonly aria-haspopup="dialog"
|
|
2807
|
+
aria-controls="cal" value="15 de julio de 2026">
|
|
2808
|
+
</div>
|
|
2809
|
+
<div class="datepicker__calendar" id="cal" role="dialog" aria-label="Elegir fecha" hidden>
|
|
2810
|
+
<div class="calendar">
|
|
2811
|
+
<div class="calendar__header">
|
|
2812
|
+
<button class="icon-btn icon-btn--ghost icon-btn--sm" aria-label="Mes anterior">‹</button>
|
|
2813
|
+
<span class="calendar__title" aria-live="polite">julio 2026</span>
|
|
2814
|
+
<button class="icon-btn icon-btn--ghost icon-btn--sm" aria-label="Mes siguiente">›</button>
|
|
2815
|
+
</div>
|
|
2816
|
+
<div class="calendar__table" role="grid" aria-label="julio 2026">
|
|
2817
|
+
<div class="calendar__weekdays" role="row">
|
|
2818
|
+
<span role="columnheader" aria-label="lunes">L</span> …
|
|
2819
|
+
</div>
|
|
2820
|
+
<div class="calendar__grid" role="rowgroup">
|
|
2821
|
+
<div class="calendar__week" role="row">
|
|
2822
|
+
<button class="calendar__day" role="gridcell"
|
|
2823
|
+
aria-label="15 de julio de 2026" aria-selected="true">15</button> …
|
|
2824
|
+
</div>
|
|
2825
|
+
</div>
|
|
2826
|
+
</div>
|
|
2827
|
+
</div>
|
|
2828
|
+
</div>
|
|
2829
|
+
</div>
|
|
2830
|
+
<!-- JS: clic abre; flechas mueven el día (±1, ±7) cruzando meses; Enter elige; Esc cierra. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2831
|
+
</section>
|
|
2832
|
+
|
|
2833
|
+
<!-- ================= FILE UPLOAD ================= -->
|
|
2834
|
+
<section id="comp-fileupload">
|
|
2835
|
+
<h2>FileUpload</h2>
|
|
2836
|
+
<p>Zona de arrastre con lista de archivos. El input file real va oculto accesiblemente (<span class="code">.visually-hidden</span>, sigue tabulable) y el ring de foco se pinta sobre el label. Al arrastrar un archivo encima, <span class="code">.is-dragover</span> tiñe el borde de acento. Un ítem subiendo incrusta el <span class="code">.progress</span>; el error se marca con icono + texto, no solo por color.</p>
|
|
2837
|
+
<div class="ds-demo">
|
|
2838
|
+
<div class="file-upload" id="fileUpload" style="width:100%;max-width:520px;">
|
|
2839
|
+
<input class="file-upload__input visually-hidden" type="file" id="fuInput" multiple aria-describedby="fuHint">
|
|
2840
|
+
<label class="file-upload__label" for="fuInput">
|
|
2841
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v13"/></svg>
|
|
2842
|
+
<span>Arrastra archivos aquí o <span class="file-upload__button">explora</span></span>
|
|
2843
|
+
<span class="file-upload__hint" id="fuHint">PNG, JPG o PDF · máximo 10 MB</span>
|
|
2844
|
+
</label>
|
|
2845
|
+
<ul class="file-upload__list" id="fuList">
|
|
2846
|
+
<li class="file-upload__item">
|
|
2847
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
|
|
2848
|
+
<div>
|
|
2849
|
+
<span class="file-upload__name">informe-trimestral-q3.pdf</span>
|
|
2850
|
+
<span class="file-upload__size">2.4 MB · subiendo…</span>
|
|
2851
|
+
<div class="progress" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" aria-label="Subiendo informe-trimestral-q3.pdf"><div class="progress__bar" style="width:60%;"></div></div>
|
|
2852
|
+
</div>
|
|
2853
|
+
<button class="file-upload__remove" type="button" aria-label="Cancelar informe-trimestral-q3.pdf"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
2854
|
+
</li>
|
|
2855
|
+
<li class="file-upload__item">
|
|
2856
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
|
|
2857
|
+
<div>
|
|
2858
|
+
<span class="file-upload__name">mockup-portada.png</span>
|
|
2859
|
+
<span class="file-upload__size">840 KB · completo</span>
|
|
2860
|
+
</div>
|
|
2861
|
+
<button class="file-upload__remove" type="button" aria-label="Quitar mockup-portada.png"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
2862
|
+
</li>
|
|
2863
|
+
<li class="file-upload__item file-upload__item--error">
|
|
2864
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 8v4m0 4h.01"/></svg>
|
|
2865
|
+
<div>
|
|
2866
|
+
<span class="file-upload__name">video-demo.mov</span>
|
|
2867
|
+
<span class="file-upload__size">Error: supera los 10 MB</span>
|
|
2868
|
+
</div>
|
|
2869
|
+
<button class="file-upload__remove" type="button" aria-label="Quitar video-demo.mov"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
2870
|
+
</li>
|
|
2871
|
+
</ul>
|
|
2872
|
+
</div>
|
|
2873
|
+
</div>
|
|
2874
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><div class="file-upload">
|
|
2875
|
+
<input class="file-upload__input visually-hidden" type="file" id="f" multiple
|
|
2876
|
+
aria-describedby="hint">
|
|
2877
|
+
<label class="file-upload__label" for="f">
|
|
2878
|
+
<svg aria-hidden="true">…subir…</svg>
|
|
2879
|
+
<span>Arrastra archivos aquí o <span class="file-upload__button">explora</span></span>
|
|
2880
|
+
<span class="file-upload__hint" id="hint">PNG, JPG o PDF · máximo 10 MB</span>
|
|
2881
|
+
</label>
|
|
2882
|
+
<ul class="file-upload__list">
|
|
2883
|
+
<li class="file-upload__item">
|
|
2884
|
+
<svg aria-hidden="true">…archivo…</svg>
|
|
2885
|
+
<div>
|
|
2886
|
+
<span class="file-upload__name">informe.pdf</span>
|
|
2887
|
+
<span class="file-upload__size">2.4 MB · subiendo…</span>
|
|
2888
|
+
<div class="progress" role="progressbar" aria-valuenow="60" …>
|
|
2889
|
+
<div class="progress__bar" style="width:60%"></div></div>
|
|
2890
|
+
</div>
|
|
2891
|
+
<button class="file-upload__remove" aria-label="Cancelar informe.pdf">×</button>
|
|
2892
|
+
</li>
|
|
2893
|
+
<li class="file-upload__item file-upload__item--error">…icono + texto de error…</li>
|
|
2894
|
+
</ul>
|
|
2895
|
+
</div>
|
|
2896
|
+
<!-- JS: dragover añade .is-dragover; el botón × quita el ítem. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2897
|
+
</section>
|
|
2898
|
+
|
|
2899
|
+
<!-- ================= STEPPER ================= -->
|
|
2900
|
+
<section id="comp-stepper">
|
|
2901
|
+
<h2>Stepper / Wizard</h2>
|
|
2902
|
+
<p>Indicador de progreso por pasos. Es un <span class="code"><ol></span>, así que el orden y el conteo se anuncian solos; el paso en curso lleva <span class="code">aria-current="step"</span> y los pasos completos muestran un check (estado no solo por color). El conector se tiñe de acento en el tramo ya recorrido. Variante <span class="code">.stepper--vertical</span> para apilarlos.</p>
|
|
2903
|
+
<div class="ds-demo ds-demo--grid" style="align-items:start;">
|
|
2904
|
+
<ol class="stepper" aria-label="Progreso de la configuración" style="width:100%;">
|
|
2905
|
+
<li class="step step--complete">
|
|
2906
|
+
<span class="step__marker"><span class="visually-hidden">Completado: </span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></span>
|
|
2907
|
+
<span class="step__label">Cuenta</span>
|
|
2908
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2909
|
+
</li>
|
|
2910
|
+
<li class="step step--complete">
|
|
2911
|
+
<span class="step__marker"><span class="visually-hidden">Completado: </span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></span>
|
|
2912
|
+
<span class="step__label">Perfil</span>
|
|
2913
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2914
|
+
</li>
|
|
2915
|
+
<li class="step step--current" aria-current="step">
|
|
2916
|
+
<span class="step__marker"><span class="visually-hidden">Paso actual: </span>3</span>
|
|
2917
|
+
<span class="step__label">Pago</span>
|
|
2918
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2919
|
+
</li>
|
|
2920
|
+
<li class="step step--upcoming">
|
|
2921
|
+
<span class="step__marker"><span class="visually-hidden">Pendiente: </span>4</span>
|
|
2922
|
+
<span class="step__label">Confirmación</span>
|
|
2923
|
+
</li>
|
|
2924
|
+
</ol>
|
|
2925
|
+
<ol class="stepper stepper--vertical" aria-label="Progreso del despliegue">
|
|
2926
|
+
<li class="step step--complete">
|
|
2927
|
+
<span class="step__marker"><span class="visually-hidden">Completado: </span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></span>
|
|
2928
|
+
<span class="step__label">Compilar el proyecto</span>
|
|
2929
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2930
|
+
</li>
|
|
2931
|
+
<li class="step step--current" aria-current="step">
|
|
2932
|
+
<span class="step__marker"><span class="visually-hidden">Paso actual: </span>2</span>
|
|
2933
|
+
<span class="step__label">Ejecutar pruebas</span>
|
|
2934
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2935
|
+
</li>
|
|
2936
|
+
<li class="step step--upcoming">
|
|
2937
|
+
<span class="step__marker"><span class="visually-hidden">Pendiente: </span>3</span>
|
|
2938
|
+
<span class="step__label">Publicar en producción</span>
|
|
2939
|
+
</li>
|
|
2940
|
+
</ol>
|
|
2941
|
+
</div>
|
|
2942
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code><ol class="stepper" aria-label="Progreso de la configuración">
|
|
2943
|
+
<li class="step step--complete">
|
|
2944
|
+
<span class="step__marker"><svg aria-hidden="true">…check…</svg></span>
|
|
2945
|
+
<span class="step__label">Cuenta</span>
|
|
2946
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2947
|
+
</li>
|
|
2948
|
+
<li class="step step--current" aria-current="step">
|
|
2949
|
+
<span class="step__marker">3</span>
|
|
2950
|
+
<span class="step__label">Pago</span>
|
|
2951
|
+
<span class="step__connector" aria-hidden="true"></span>
|
|
2952
|
+
</li>
|
|
2953
|
+
<li class="step step--upcoming">
|
|
2954
|
+
<span class="step__marker">4</span><span class="step__label">Confirmación</span>
|
|
2955
|
+
</li>
|
|
2956
|
+
</ol>
|
|
2957
|
+
<!-- El último paso no lleva conector. Variante: .stepper--vertical. --></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2958
|
+
</section>
|
|
2959
|
+
|
|
2960
|
+
<!-- ================= KBD ================= -->
|
|
2961
|
+
<section id="comp-kbd">
|
|
2962
|
+
<h2>Kbd</h2>
|
|
2963
|
+
<p>Tecla o atajo de teclado sobre <span class="code"><kbd></span>: JetBrains Mono, borde <span class="code">--border-strong</span> y sombra sutil, alto y ancho mínimo 20px. Para combos se encadenan varias con un "+" literal. Es informativa, nunca un botón.</p>
|
|
2964
|
+
<div class="ds-demo" style="gap:var(--space-4);align-items:center;">
|
|
2965
|
+
<span style="display:inline-flex;align-items:center;gap:6px;"><kbd class="kbd">Esc</kbd></span>
|
|
2966
|
+
<span style="display:inline-flex;align-items:center;gap:6px;"><kbd class="kbd">Tab</kbd></span>
|
|
2967
|
+
<span style="display:inline-flex;align-items:center;gap:6px;"><kbd class="kbd">↵</kbd></span>
|
|
2968
|
+
<span style="display:inline-flex;align-items:center;gap:6px;color:var(--text-muted);"><kbd class="kbd">⌘</kbd> + <kbd class="kbd">K</kbd></span>
|
|
2969
|
+
<span style="display:inline-flex;align-items:center;gap:6px;color:var(--text-muted);"><kbd class="kbd">Ctrl</kbd> + <kbd class="kbd">S</kbd></span>
|
|
2970
|
+
<span style="display:inline-flex;align-items:center;gap:6px;color:var(--text-muted);"><kbd class="kbd">Shift</kbd> + <kbd class="kbd">⇥</kbd></span>
|
|
2971
|
+
</div>
|
|
2972
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code>Pulsa <kbd class="kbd">Esc</kbd> para cerrar.
|
|
2973
|
+
|
|
2974
|
+
Abre el buscador con <kbd class="kbd">⌘</kbd> + <kbd class="kbd">K</kbd>.
|
|
2975
|
+
Guarda con <kbd class="kbd">Ctrl</kbd> + <kbd class="kbd">S</kbd>.</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2008
2976
|
</section>
|
|
2009
2977
|
|
|
2010
2978
|
<!-- ================= MOTION ================= -->
|
|
@@ -2042,16 +3010,22 @@ html { scroll-behavior: smooth; }
|
|
|
2042
3010
|
<table class="table">
|
|
2043
3011
|
<thead><tr><th>Combinación</th><th class="num">Contraste</th><th>Veredicto</th></tr></thead>
|
|
2044
3012
|
<tbody>
|
|
2045
|
-
<tr><td>night-100 sobre night-950</td><td class="num"
|
|
2046
|
-
<tr><td>accent-400 sobre night-950</td><td class="num"
|
|
2047
|
-
<tr><td>night-950 sobre accent-400 (CTA)</td><td class="num"
|
|
2048
|
-
<tr><td>indigo-500 sobre night-950</td><td class="num"
|
|
2049
|
-
<tr><td>night-50 sobre indigo-500</td><td class="num"
|
|
3013
|
+
<tr><td>night-100 sobre night-950</td><td class="num">17.25:1</td><td><span class="badge badge--success">AAA</span></td></tr>
|
|
3014
|
+
<tr><td>accent-400 sobre night-950</td><td class="num">9.50:1</td><td><span class="badge badge--success">AA — sirve como texto/link</span></td></tr>
|
|
3015
|
+
<tr><td>night-950 sobre accent-400 (CTA)</td><td class="num">9.50:1</td><td><span class="badge badge--success">AA — texto del CTA oscuro</span></td></tr>
|
|
3016
|
+
<tr><td>indigo-500 sobre night-950</td><td class="num">2.09:1</td><td><span class="badge badge--danger">Solo fondo, nunca texto</span></td></tr>
|
|
3017
|
+
<tr><td>night-50 sobre indigo-500</td><td class="num">9.29:1</td><td><span class="badge badge--success">AAA</span></td></tr>
|
|
3018
|
+
<tr><td>accent-600 sobre blanco <em>(modo claro)</em></td><td class="num">4.01:1</td><td><span class="badge badge--warning">Solo UI — nunca texto</span></td></tr>
|
|
3019
|
+
<tr><td>accent-700 sobre blanco <em>(texto de acción en claro)</em></td><td class="num">5.95:1</td><td><span class="badge badge--success">AA</span></td></tr>
|
|
3020
|
+
<tr><td>accent-800 sobre blanco <em>(hover en claro)</em></td><td class="num">8.74:1</td><td><span class="badge badge--success">AAA</span></td></tr>
|
|
3021
|
+
<tr><td>info sobre info-bg <em>(badge en claro)</em></td><td class="num">5.20:1</td><td><span class="badge badge--success">AA</span></td></tr>
|
|
2050
3022
|
</tbody>
|
|
2051
3023
|
</table>
|
|
2052
3024
|
</div>
|
|
2053
3025
|
<ul style="color:var(--text-secondary);line-height:1.9;font-size:var(--text-sm);">
|
|
2054
3026
|
<li>Mínimos WCAG AA: 4.5:1 texto normal, 3:1 texto grande y componentes UI.</li>
|
|
3027
|
+
<li>Ratios <strong>calculados</strong> con la fórmula WCAG 2.x, no estimados: <span class="code">mcp/test/contraste-tokens.test.ts</span> los recalcula desde <span class="code">tokens.css</span> en cada corrida.</li>
|
|
3028
|
+
<li>En modo claro, <span class="code">accent-600</span> (<span class="code">#1f87bb</span>) vale como <strong>UI</strong> (focus, bordes, iconos) pero <strong>no como texto</strong>: da 4.01:1. El texto de acción usa <span class="code">accent-700</span>.</li>
|
|
2055
3029
|
<li>Focus visible siempre (<span class="code">:focus-visible</span> global); estado nunca solo con color — siempre color + icono + texto.</li>
|
|
2056
3030
|
<li>Touch targets de 44×44px. IconButton siempre con <span class="code">aria-label</span>.</li>
|
|
2057
3031
|
<li><span class="code">role="progressbar"</span> en Progress, <span class="code">aria-expanded</span> en Accordion, <span class="code">aria-current</span> en navegación.</li>
|
|
@@ -2064,11 +3038,11 @@ html { scroll-behavior: smooth; }
|
|
|
2064
3038
|
<p>El sistema se distribuye en <span class="code">dist/nocturna-electrica/</span>: <strong>nocturna-electrica.css</strong> (framework completo), <strong>tokens.css</strong> (solo variables), <strong>tailwind.css</strong> (bloque <span class="code">@theme</span> para Tailwind v4) y <strong>tokens.json</strong> (formato estándar W3C DTCG 2025.10, interoperable con Figma / Tokens Studio / Style Dictionary).</p>
|
|
2065
3039
|
|
|
2066
3040
|
<h3>Variables CSS (núcleo)</h3>
|
|
2067
|
-
<details class="ds-code" open><summary>tokens :root (aliases semánticos)
|
|
3041
|
+
<div class="ds-code-wrap"><details class="ds-code" open><summary>tokens :root (aliases semánticos)</summary><pre><code>:root {
|
|
2068
3042
|
/* Superficies */
|
|
2069
3043
|
--bg: #010203;
|
|
2070
3044
|
--surface-1: #0b0d12; --surface-2: #14171f; --surface-3: #1e222d;
|
|
2071
|
-
--border: #3d4354; --border-strong: #
|
|
3045
|
+
--border: #3d4354; --border-strong: #6b7183;
|
|
2072
3046
|
|
|
2073
3047
|
/* Texto */
|
|
2074
3048
|
--text-primary: #e8eaef; --text-secondary: #c5c9d4;
|
|
@@ -2102,10 +3076,10 @@ html { scroll-behavior: smooth; }
|
|
|
2102
3076
|
--ease-out: cubic-bezier(0.2, 0, 0, 1);
|
|
2103
3077
|
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
2104
3078
|
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
2105
|
-
}</code></pre></details>
|
|
3079
|
+
}</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2106
3080
|
|
|
2107
3081
|
<h3 style="margin-top:32px;">Tailwind CSS v4 — <span class="code">@theme</span></h3>
|
|
2108
|
-
<
|
|
3082
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>tailwind.css</summary><pre><code>@import "tailwindcss";
|
|
2109
3083
|
|
|
2110
3084
|
@theme {
|
|
2111
3085
|
--color-night-950: #010203; --color-night-900: #0b0d12;
|
|
@@ -2117,7 +3091,7 @@ html { scroll-behavior: smooth; }
|
|
|
2117
3091
|
--color-accent-100: #d9f0fb; --color-accent-200: #a8ddf6;
|
|
2118
3092
|
--color-accent-300: #7dccf1; --color-accent-400: #52baed;
|
|
2119
3093
|
--color-accent-500: #2fa3dd; --color-accent-600: #1f87bb;
|
|
2120
|
-
--color-accent-700: #186a94;
|
|
3094
|
+
--color-accent-700: #186a94; --color-accent-800: #12506f;
|
|
2121
3095
|
--color-indigo-300: #7a83c4; --color-indigo-400: #565fa3;
|
|
2122
3096
|
--color-indigo-500: #353d7c; --color-indigo-600: #2b3264;
|
|
2123
3097
|
--color-indigo-700: #22284f; --color-indigo-800: #1a1e3b;
|
|
@@ -2139,7 +3113,7 @@ html { scroll-behavior: smooth; }
|
|
|
2139
3113
|
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
2140
3114
|
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
2141
3115
|
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
2142
|
-
}</code></pre></details>
|
|
3116
|
+
}</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2143
3117
|
|
|
2144
3118
|
<div class="ds-callout ds-callout--info" style="margin-top:32px;">
|
|
2145
3119
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4m0-4h.01"/></svg>
|
|
@@ -2150,33 +3124,44 @@ html { scroll-behavior: smooth; }
|
|
|
2150
3124
|
<!-- ================= INSTALACIÓN ================= -->
|
|
2151
3125
|
<section id="instalacion">
|
|
2152
3126
|
<h2>Instalación</h2>
|
|
2153
|
-
<p>El sistema se distribuye como archivos en <span class="code">dist/nocturna-electrica/</span
|
|
3127
|
+
<p>El sistema se distribuye como paquete npm (<span class="code">@higinio/design-css</span>) y también como archivos sueltos en <span class="code">dist/nocturna-electrica/</span>. Instálalo o cópialos, y elige qué importar según tu stack (A/B/C/D). En todas: <strong>dark por defecto</strong>; <span class="code"><html data-theme="light"></span> activa el modo claro, y los componentes consumen <strong>solo aliases semánticos</strong>, nunca hex.</p>
|
|
3128
|
+
|
|
3129
|
+
<h3 style="margin-top:32px;">Instalar desde npm (recomendado)</h3>
|
|
3130
|
+
<p>Un solo paquete con todos los sistemas, cada uno en su subruta; Nocturna Eléctrica vive en <span class="code">/nocturna/</span>. Instálalo y usa las vías de abajo apuntando a la subruta, en vez de a un archivo copiado.</p>
|
|
3131
|
+
<div class="ds-code-wrap"><details class="ds-code" open><summary>terminal + CSS de entrada</summary><pre><code>npm i @higinio/design-css
|
|
3132
|
+
|
|
3133
|
+
/* framework completo (vía A) */
|
|
3134
|
+
@import "@higinio/design-css/nocturna/framework.css";
|
|
3135
|
+
/* o solo tokens (vía B) */
|
|
3136
|
+
@import "@higinio/design-css/nocturna/tokens.css";
|
|
3137
|
+
/* o Tailwind v4 (vía C) */
|
|
3138
|
+
@import "@higinio/design-css/nocturna/tailwind.css";</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2154
3139
|
|
|
2155
3140
|
<h3 style="margin-top:32px;">A · CSS drop-in (framework completo)</h3>
|
|
2156
3141
|
<p>Para cualquier proyecto — HTML plano o cualquier framework. Trae variables + clases de componentes listas (tipo Bootstrap) y ya importa las fuentes.</p>
|
|
2157
|
-
<
|
|
3142
|
+
<div class="ds-code-wrap"><details class="ds-code" open><summary>index.html</summary><pre><code><link rel="stylesheet" href="/estilos/nocturna-electrica.css">
|
|
2158
3143
|
<!-- luego usas las clases directo: -->
|
|
2159
3144
|
<button class="btn btn--primary">Guardar</button>
|
|
2160
|
-
<div class="card">…</div></code></pre></details>
|
|
3145
|
+
<div class="card">…</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2161
3146
|
|
|
2162
3147
|
<h3 style="margin-top:32px;">B · Solo tokens</h3>
|
|
2163
3148
|
<p>Si diseñas tus propios componentes: solo las variables, sin clases.</p>
|
|
2164
|
-
<
|
|
3149
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>styles.css</summary><pre><code>@import "./tokens.css";
|
|
2165
3150
|
|
|
2166
3151
|
.mi-panel {
|
|
2167
3152
|
background: var(--surface-1);
|
|
2168
3153
|
color: var(--text-primary);
|
|
2169
3154
|
border: 1px solid var(--border);
|
|
2170
|
-
}</code></pre></details>
|
|
3155
|
+
}</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2171
3156
|
|
|
2172
3157
|
<h3 style="margin-top:32px;">C · Tailwind v4</h3>
|
|
2173
3158
|
<p>Tailwind v4 se configura por CSS. El <span class="code">tailwind.css</span> ya incluye <span class="code">@import "tailwindcss"</span>, los tokens y el bloque <span class="code">@theme</span> — úsalo como tu hoja de entrada (mantén <span class="code">tokens.css</span> a su lado, que es lo que importa).</p>
|
|
2174
|
-
<details class="ds-code"><summary>terminal + CSS de entrada
|
|
3159
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>terminal + CSS de entrada</summary><pre><code># 1) instala Tailwind v4
|
|
2175
3160
|
npm i tailwindcss @tailwindcss/postcss
|
|
2176
3161
|
|
|
2177
3162
|
# 2) copia tailwind.css + tokens.css a tu carpeta de CSS
|
|
2178
3163
|
# y usa tailwind.css como entrada. Ya tienes utilidades:
|
|
2179
|
-
<div class="bg-night-900 text-accent-400">…</div></code></pre></details>
|
|
3164
|
+
<div class="bg-night-900 text-accent-400">…</div></code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2180
3165
|
|
|
2181
3166
|
<h3 style="margin-top:32px;">D · Pipelines de tokens</h3>
|
|
2182
3167
|
<p>Para Figma, Tokens Studio o Style Dictionary: importa <span class="code">tokens.json</span> (formato estándar W3C DTCG 2025.10).</p>
|
|
@@ -2190,20 +3175,23 @@ npm i tailwindcss @tailwindcss/postcss
|
|
|
2190
3175
|
<!-- ================= MCP ================= -->
|
|
2191
3176
|
<section id="mcp">
|
|
2192
3177
|
<h2>MCP</h2>
|
|
2193
|
-
<p>El paquete <span class="code">@higinio/design-mcp</span> expone
|
|
3178
|
+
<p>El paquete <span class="code">@higinio/design-mcp</span> expone los sistemas de diseño de Higinio Sosa, entre ellos Nocturna Eléctrica (<span class="code">system="nocturna"</span>), a cualquier IDE o cliente compatible con Model Context Protocol: tokens, componentes, reglas duras y validación de código. Complementa a las skills de Claude, no las reemplaza: misma fuente de verdad, accesible también fuera del entorno de Claude.</p>
|
|
2194
3179
|
|
|
2195
|
-
<h3>Las
|
|
3180
|
+
<h3>Las 11 tools</h3>
|
|
2196
3181
|
<div class="ds-demo ds-demo--col" style="padding:0;overflow-x:auto;">
|
|
2197
3182
|
<table class="table">
|
|
2198
3183
|
<thead><tr><th>Tool</th><th>Qué hace</th></tr></thead>
|
|
2199
3184
|
<tbody>
|
|
2200
3185
|
<tr><td class="mono">list_design_systems</td><td>Lista los sistemas de diseño disponibles.</td></tr>
|
|
2201
|
-
<tr><td class="mono">get_token</td><td>
|
|
3186
|
+
<tr><td class="mono">get_token</td><td>Resuelve un alias semántico o un token DTCG por path a su valor.</td></tr>
|
|
2202
3187
|
<tr><td class="mono">search_tokens</td><td>Busca tokens por nombre, categoría o valor.</td></tr>
|
|
2203
3188
|
<tr><td class="mono">list_components</td><td>Lista los componentes documentados de un sistema.</td></tr>
|
|
2204
3189
|
<tr><td class="mono">get_component_spec</td><td>Devuelve la especificación completa de un componente: estados, medidas, variantes.</td></tr>
|
|
2205
3190
|
<tr><td class="mono">get_rules</td><td>Devuelve las reglas duras (no negociables) del sistema.</td></tr>
|
|
2206
3191
|
<tr><td class="mono">validate_code</td><td>Audita CSS/HTML contra las reglas del sistema — hex directos, contraste, aliases.</td></tr>
|
|
3192
|
+
<tr><td class="mono">validate_motion</td><td>Audita el motion contra las reglas del sistema: <span class="code">transition</span> sobre todas las propiedades, ease-in en entradas, propiedades de layout, duraciones por encima de <span class="code">--duration-page</span>.</td></tr>
|
|
3193
|
+
<tr><td class="mono">check_slop</td><td>Detecta clichés de diseño generado por IA: gradientes, glassmorphism, sombras de negro puro, fuentes genéricas y copy de marketing.</td></tr>
|
|
3194
|
+
<tr><td class="mono">checklist_preflight</td><td>Checklist estático de pre-producción sobre HTML/CSS: metadatos, headings, imágenes y accesibilidad de controles.</td></tr>
|
|
2207
3195
|
<tr><td class="mono">generate_snippet</td><td>Genera un snippet de componente listo para usar, con los aliases correctos.</td></tr>
|
|
2208
3196
|
</tbody>
|
|
2209
3197
|
</table>
|
|
@@ -2211,7 +3199,7 @@ npm i tailwindcss @tailwindcss/postcss
|
|
|
2211
3199
|
|
|
2212
3200
|
<h3 style="margin-top:32px;">Conectar (remoto)</h3>
|
|
2213
3201
|
<p>Añade el servidor a tu <span class="code">.mcp.json</span>:</p>
|
|
2214
|
-
<details class="ds-code" open><summary>.mcp.json
|
|
3202
|
+
<div class="ds-code-wrap"><details class="ds-code" open><summary>.mcp.json</summary><pre><code>{
|
|
2215
3203
|
"mcpServers": {
|
|
2216
3204
|
"design": {
|
|
2217
3205
|
"type": "http",
|
|
@@ -2219,15 +3207,15 @@ npm i tailwindcss @tailwindcss/postcss
|
|
|
2219
3207
|
"headers": { "Authorization": "Bearer <tu-token>" }
|
|
2220
3208
|
}
|
|
2221
3209
|
}
|
|
2222
|
-
}</code></pre></details>
|
|
3210
|
+
}</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2223
3211
|
|
|
2224
3212
|
<h3 style="margin-top:32px;">Ejemplo</h3>
|
|
2225
3213
|
<p>Consultar un token de este sistema:</p>
|
|
2226
|
-
<
|
|
2227
|
-
→ "#52baed"</code></pre></details>
|
|
3214
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>get_token</summary><pre><code>get_token(system="nocturna", name="--accent")
|
|
3215
|
+
→ "#52baed"</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2228
3216
|
<p>Auditar código antes de integrarlo:</p>
|
|
2229
|
-
<
|
|
2230
|
-
→ { ok: false, issues: ["hex directo #52baed en línea 12 — usa var(--accent-400)"] }</code></pre></details>
|
|
3217
|
+
<div class="ds-code-wrap"><details class="ds-code"><summary>validate_code</summary><pre><code>validate_code(system="nocturna", code="…")
|
|
3218
|
+
→ { ok: false, issues: ["hex directo #52baed en línea 12 — usa var(--accent-400)"] }</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
|
|
2231
3219
|
|
|
2232
3220
|
<div class="ds-callout ds-callout--info" style="margin-top:32px;">
|
|
2233
3221
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4m0-4h.01"/></svg>
|
|
@@ -2242,14 +3230,14 @@ npm i tailwindcss @tailwindcss/postcss
|
|
|
2242
3230
|
|
|
2243
3231
|
<div class="toast-stack" id="toastStack" role="status" aria-live="polite"></div>
|
|
2244
3232
|
<div class="modal-overlay" id="demoModal" hidden>
|
|
2245
|
-
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="demoModalTitle">
|
|
3233
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="demoModalTitle" aria-describedby="demoModalDesc" tabindex="-1">
|
|
2246
3234
|
<div class="modal__header">
|
|
2247
3235
|
<h4 class="modal__title" id="demoModalTitle">Archivar proyecto</h4>
|
|
2248
3236
|
<button class="icon-btn icon-btn--ghost icon-btn--sm" data-close-modal aria-label="Cerrar">
|
|
2249
3237
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
2250
3238
|
</button>
|
|
2251
3239
|
</div>
|
|
2252
|
-
<p style="font-size:var(--text-sm);color:var(--text-secondary);">El proyecto dejará de aparecer en el panel, pero conservará sus datos. Puedes restaurarlo cuando quieras.</p>
|
|
3240
|
+
<p id="demoModalDesc" style="font-size:var(--text-sm);color:var(--text-secondary);">El proyecto dejará de aparecer en el panel, pero conservará sus datos. Puedes restaurarlo cuando quieras.</p>
|
|
2253
3241
|
<div class="modal__footer">
|
|
2254
3242
|
<button class="btn btn--outline" data-close-modal>Cancelar</button>
|
|
2255
3243
|
<button class="btn btn--primary" data-close-modal>Archivar</button>
|
|
@@ -2257,7 +3245,7 @@ npm i tailwindcss @tailwindcss/postcss
|
|
|
2257
3245
|
</div>
|
|
2258
3246
|
</div>
|
|
2259
3247
|
<div class="modal-overlay" id="demoModalLg" hidden>
|
|
2260
|
-
<div class="modal modal--lg" role="dialog" aria-modal="true" aria-labelledby="demoModalLgTitle">
|
|
3248
|
+
<div class="modal modal--lg" role="dialog" aria-modal="true" aria-labelledby="demoModalLgTitle" tabindex="-1">
|
|
2261
3249
|
<div class="modal__header">
|
|
2262
3250
|
<h4 class="modal__title" id="demoModalLgTitle">Invitar al equipo</h4>
|
|
2263
3251
|
<button class="icon-btn icon-btn--ghost icon-btn--sm" data-close-modal aria-label="Cerrar">
|
|
@@ -2418,10 +3406,9 @@ spaces.forEach(function(s){
|
|
|
2418
3406
|
|
|
2419
3407
|
/* ---------- Copiar código ---------- */
|
|
2420
3408
|
document.querySelectorAll('.ds-copy').forEach(function(btn){
|
|
2421
|
-
btn.addEventListener('click', function(
|
|
2422
|
-
|
|
2423
|
-
var
|
|
2424
|
-
var code = details ? details.querySelector('code') : null;
|
|
3409
|
+
btn.addEventListener('click', function(){
|
|
3410
|
+
var box = btn.closest('.ds-code-wrap');
|
|
3411
|
+
var code = box ? box.querySelector('code') : null;
|
|
2425
3412
|
if (code) {
|
|
2426
3413
|
copyText(code.textContent, function(){
|
|
2427
3414
|
btn.textContent = '¡Copiado!'; btn.classList.add('is-done');
|
|
@@ -2512,6 +3499,31 @@ tabs.querySelectorAll('.tabs__btn').forEach(function(b){
|
|
|
2512
3499
|
});
|
|
2513
3500
|
setTimeout(function(){ moveIndicator(tabs.querySelector('.is-active')); }, 100);
|
|
2514
3501
|
|
|
3502
|
+
/* ---------- Sidebar contraíble: riel de iconos ---------- */
|
|
3503
|
+
document.querySelectorAll('.sidebar__toggle').forEach(function(btn){
|
|
3504
|
+
var nav = document.getElementById(btn.getAttribute('aria-controls'));
|
|
3505
|
+
var label = btn.querySelector('.sidebar__label');
|
|
3506
|
+
if (!nav) return;
|
|
3507
|
+
btn.addEventListener('click', function(){
|
|
3508
|
+
var collapsed = nav.classList.toggle('is-collapsed');
|
|
3509
|
+
btn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|
3510
|
+
if (label) label.textContent = collapsed ? 'Expandir' : 'Contraer';
|
|
3511
|
+
});
|
|
3512
|
+
});
|
|
3513
|
+
|
|
3514
|
+
/* ---------- Chip de métrica: sumar un vaso ---------- */
|
|
3515
|
+
(function(){
|
|
3516
|
+
var chip = document.getElementById('dsWaterChip');
|
|
3517
|
+
if (!chip) return;
|
|
3518
|
+
var value = chip.querySelector('.metric-chip__value');
|
|
3519
|
+
var max = 8, n = 3;
|
|
3520
|
+
chip.addEventListener('click', function(){
|
|
3521
|
+
n = n >= max ? 0 : n + 1;
|
|
3522
|
+
value.textContent = n + ' de ' + max;
|
|
3523
|
+
chip.setAttribute('aria-label', 'Agua: ' + n + ' de ' + max + ' vasos. ' + (n < max ? 'Sumar un vaso' : 'Empezar de nuevo'));
|
|
3524
|
+
});
|
|
3525
|
+
})();
|
|
3526
|
+
|
|
2515
3527
|
/* ---------- Alert ---------- */
|
|
2516
3528
|
document.querySelectorAll('.js-alert-close').forEach(function(btn){
|
|
2517
3529
|
btn.addEventListener('click', function(){
|
|
@@ -2565,22 +3577,272 @@ document.querySelectorAll('[data-toast]').forEach(function(b){
|
|
|
2565
3577
|
});
|
|
2566
3578
|
});
|
|
2567
3579
|
|
|
2568
|
-
/* ---------- Modal ---------- */
|
|
2569
|
-
|
|
3580
|
+
/* ---------- Modal (focus-trap + retorno de foco + Esc) ---------- */
|
|
3581
|
+
var FOCUSABLE_SEL = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
3582
|
+
function getFocusable(container){
|
|
3583
|
+
return Array.prototype.slice.call(container.querySelectorAll(FOCUSABLE_SEL))
|
|
3584
|
+
.filter(function(el){ return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length; });
|
|
3585
|
+
}
|
|
3586
|
+
function wireModal(triggerId, overlayId){
|
|
2570
3587
|
var trigger = document.getElementById(triggerId);
|
|
2571
|
-
var
|
|
2572
|
-
if (!trigger || !
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
3588
|
+
var overlay = document.getElementById(overlayId);
|
|
3589
|
+
if (!trigger || !overlay) return;
|
|
3590
|
+
var panel = overlay.querySelector('.modal');
|
|
3591
|
+
var persistent = overlay.hasAttribute('data-modal-persistent'); /* flujos destructivos */
|
|
3592
|
+
var lastFocused = null;
|
|
3593
|
+
|
|
3594
|
+
function open(){
|
|
3595
|
+
lastFocused = document.activeElement;
|
|
3596
|
+
overlay.hidden = false;
|
|
3597
|
+
if (panel) panel.focus(); /* el panel tiene tabindex="-1"; el lector anuncia el título */
|
|
3598
|
+
}
|
|
3599
|
+
function close(){
|
|
3600
|
+
if (overlay.hidden) return;
|
|
3601
|
+
overlay.hidden = true;
|
|
3602
|
+
if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus(); /* retorno de foco */
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
trigger.addEventListener('click', open);
|
|
3606
|
+
|
|
3607
|
+
overlay.addEventListener('click', function(e){
|
|
3608
|
+
if (e.target.closest('[data-close-modal]')) { close(); return; }
|
|
3609
|
+
if (e.target === overlay && !persistent) close(); /* click en overlay */
|
|
2576
3610
|
});
|
|
2577
|
-
|
|
2578
|
-
|
|
3611
|
+
|
|
3612
|
+
overlay.addEventListener('keydown', function(e){
|
|
3613
|
+
if (e.key === 'Escape' && !persistent) { e.stopPropagation(); close(); return; }
|
|
3614
|
+
if (e.key !== 'Tab' || !panel) return;
|
|
3615
|
+
var f = getFocusable(panel);
|
|
3616
|
+
if (!f.length) { e.preventDefault(); return; }
|
|
3617
|
+
var first = f[0], last = f[f.length - 1], active = document.activeElement;
|
|
3618
|
+
if (f.indexOf(active) === -1) { /* foco en el panel (tabindex=-1): entra al extremo correcto */
|
|
3619
|
+
e.preventDefault(); (e.shiftKey ? last : first).focus(); return;
|
|
3620
|
+
}
|
|
3621
|
+
if (e.shiftKey && active === first) { e.preventDefault(); last.focus(); }
|
|
3622
|
+
else if (!e.shiftKey && active === last) { e.preventDefault(); first.focus(); }
|
|
2579
3623
|
});
|
|
2580
3624
|
}
|
|
2581
3625
|
wireModal('openModal', 'demoModal');
|
|
2582
3626
|
wireModal('openModalLg', 'demoModalLg');
|
|
2583
3627
|
|
|
3628
|
+
/* ---------- Drawer (focus-trap + retorno de foco + Esc + overlay) ---------- */
|
|
3629
|
+
function wireDrawer(triggerId, drawerId){
|
|
3630
|
+
var trigger = document.getElementById(triggerId);
|
|
3631
|
+
var drawer = document.getElementById(drawerId);
|
|
3632
|
+
if (!trigger || !drawer) return;
|
|
3633
|
+
var panel = drawer.querySelector('.drawer__panel');
|
|
3634
|
+
var lastFocused = null;
|
|
3635
|
+
function open(){
|
|
3636
|
+
lastFocused = document.activeElement;
|
|
3637
|
+
drawer.hidden = false;
|
|
3638
|
+
if (trigger.hasAttribute('aria-expanded')) trigger.setAttribute('aria-expanded', 'true');
|
|
3639
|
+
/* dos pasos: pinta oculto y en el siguiente frame añade .is-open para que el transform anime */
|
|
3640
|
+
requestAnimationFrame(function(){ drawer.classList.add('is-open'); });
|
|
3641
|
+
if (panel) panel.focus(); /* tabindex="-1"; el lector anuncia el título */
|
|
3642
|
+
}
|
|
3643
|
+
function close(){
|
|
3644
|
+
if (drawer.hidden) return;
|
|
3645
|
+
drawer.classList.remove('is-open');
|
|
3646
|
+
if (trigger.hasAttribute('aria-expanded')) trigger.setAttribute('aria-expanded', 'false');
|
|
3647
|
+
var finish = function(){ drawer.hidden = true; if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus(); };
|
|
3648
|
+
if (reduced) finish(); else setTimeout(finish, 250); /* espera el slide de salida (--duration-slow) */
|
|
3649
|
+
}
|
|
3650
|
+
trigger.addEventListener('click', open);
|
|
3651
|
+
drawer.addEventListener('click', function(e){
|
|
3652
|
+
if (e.target.closest('[data-close-drawer]')) close(); /* overlay o botón de cierre */
|
|
3653
|
+
});
|
|
3654
|
+
drawer.addEventListener('keydown', function(e){
|
|
3655
|
+
if (e.key === 'Escape') { e.stopPropagation(); close(); return; }
|
|
3656
|
+
if (e.key !== 'Tab' || !panel) return;
|
|
3657
|
+
var f = getFocusable(panel);
|
|
3658
|
+
if (!f.length) { e.preventDefault(); return; }
|
|
3659
|
+
var first = f[0], last = f[f.length - 1], active = document.activeElement;
|
|
3660
|
+
if (f.indexOf(active) === -1) { e.preventDefault(); (e.shiftKey ? last : first).focus(); return; }
|
|
3661
|
+
if (e.shiftKey && active === first) { e.preventDefault(); last.focus(); }
|
|
3662
|
+
else if (!e.shiftKey && active === last) { e.preventDefault(); first.focus(); }
|
|
3663
|
+
});
|
|
3664
|
+
}
|
|
3665
|
+
wireDrawer('openDrawer', 'demoDrawer');
|
|
3666
|
+
wireDrawer('openNavDrawer', 'demoNavDrawer');
|
|
3667
|
+
|
|
3668
|
+
/* ---------- Popover (abre por CLICK; foco entra; Esc / clic fuera cierran) ---------- */
|
|
3669
|
+
(function(){
|
|
3670
|
+
var pop = document.querySelector('#comp-popover .popover');
|
|
3671
|
+
var trigger = document.getElementById('popTrigger');
|
|
3672
|
+
var panel = document.getElementById('popPanel');
|
|
3673
|
+
if (!pop || !trigger || !panel) return;
|
|
3674
|
+
function open(){
|
|
3675
|
+
pop.classList.add('is-open');
|
|
3676
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
3677
|
+
var f = getFocusable(panel);
|
|
3678
|
+
if (f.length) f[0].focus();
|
|
3679
|
+
}
|
|
3680
|
+
function close(returnFocus){
|
|
3681
|
+
if (!pop.classList.contains('is-open')) return;
|
|
3682
|
+
pop.classList.remove('is-open');
|
|
3683
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
3684
|
+
if (returnFocus) trigger.focus();
|
|
3685
|
+
}
|
|
3686
|
+
trigger.addEventListener('click', function(e){
|
|
3687
|
+
e.stopPropagation();
|
|
3688
|
+
if (pop.classList.contains('is-open')) close(true); else open();
|
|
3689
|
+
});
|
|
3690
|
+
document.addEventListener('click', function(e){
|
|
3691
|
+
if (pop.classList.contains('is-open') && !pop.contains(e.target)) close(false);
|
|
3692
|
+
});
|
|
3693
|
+
document.addEventListener('keydown', function(e){
|
|
3694
|
+
if (e.key === 'Escape' && pop.classList.contains('is-open')) close(true);
|
|
3695
|
+
});
|
|
3696
|
+
})();
|
|
3697
|
+
|
|
3698
|
+
/* ---------- Combobox (filtra en vivo + teclado ↑/↓/Enter/Esc) ---------- */
|
|
3699
|
+
(function(){
|
|
3700
|
+
var input = document.getElementById('cbInput');
|
|
3701
|
+
var list = document.getElementById('cbList');
|
|
3702
|
+
if (!input || !list) return;
|
|
3703
|
+
var options = Array.prototype.slice.call(list.querySelectorAll('.combobox__option'));
|
|
3704
|
+
var activeIndex = -1;
|
|
3705
|
+
function visible(){ return options.filter(function(o){ return !o.hidden; }); }
|
|
3706
|
+
function openList(){ list.hidden = false; input.setAttribute('aria-expanded', 'true'); }
|
|
3707
|
+
function closeList(){ list.hidden = true; input.setAttribute('aria-expanded', 'false'); setActive(-1); }
|
|
3708
|
+
function setActive(i){
|
|
3709
|
+
options.forEach(function(o){ o.classList.remove('is-active'); });
|
|
3710
|
+
var vis = visible();
|
|
3711
|
+
activeIndex = i;
|
|
3712
|
+
if (i >= 0 && i < vis.length) {
|
|
3713
|
+
vis[i].classList.add('is-active');
|
|
3714
|
+
input.setAttribute('aria-activedescendant', vis[i].id);
|
|
3715
|
+
vis[i].scrollIntoView({ block: 'nearest' });
|
|
3716
|
+
} else {
|
|
3717
|
+
input.setAttribute('aria-activedescendant', '');
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
function filter(){
|
|
3721
|
+
var q = input.value.trim().toLowerCase();
|
|
3722
|
+
options.forEach(function(o){
|
|
3723
|
+
o.hidden = o.textContent.toLowerCase().indexOf(q) === -1;
|
|
3724
|
+
});
|
|
3725
|
+
if (visible().length) openList(); else closeList();
|
|
3726
|
+
setActive(-1);
|
|
3727
|
+
}
|
|
3728
|
+
function choose(opt){
|
|
3729
|
+
options.forEach(function(o){ o.setAttribute('aria-selected', 'false'); o.hidden = false; });
|
|
3730
|
+
opt.setAttribute('aria-selected', 'true');
|
|
3731
|
+
input.value = opt.textContent.trim();
|
|
3732
|
+
closeList();
|
|
3733
|
+
input.focus();
|
|
3734
|
+
}
|
|
3735
|
+
input.addEventListener('input', filter);
|
|
3736
|
+
input.addEventListener('keydown', function(e){
|
|
3737
|
+
if (e.key === 'ArrowDown') {
|
|
3738
|
+
e.preventDefault();
|
|
3739
|
+
if (list.hidden) openList();
|
|
3740
|
+
var v = visible();
|
|
3741
|
+
setActive(activeIndex < 0 ? 0 : Math.min(activeIndex + 1, v.length - 1));
|
|
3742
|
+
} else if (e.key === 'ArrowUp') {
|
|
3743
|
+
e.preventDefault();
|
|
3744
|
+
if (list.hidden) openList();
|
|
3745
|
+
setActive(Math.max(activeIndex - 1, 0));
|
|
3746
|
+
} else if (e.key === 'Enter') {
|
|
3747
|
+
var v = visible();
|
|
3748
|
+
if (!list.hidden && activeIndex >= 0 && v[activeIndex]) { e.preventDefault(); choose(v[activeIndex]); }
|
|
3749
|
+
} else if (e.key === 'Escape') {
|
|
3750
|
+
if (!list.hidden) { e.preventDefault(); closeList(); }
|
|
3751
|
+
}
|
|
3752
|
+
});
|
|
3753
|
+
list.addEventListener('click', function(e){
|
|
3754
|
+
var opt = e.target.closest('.combobox__option');
|
|
3755
|
+
if (opt) choose(opt);
|
|
3756
|
+
});
|
|
3757
|
+
list.addEventListener('mousemove', function(e){
|
|
3758
|
+
var opt = e.target.closest('.combobox__option');
|
|
3759
|
+
if (!opt) return;
|
|
3760
|
+
var i = visible().indexOf(opt);
|
|
3761
|
+
if (i !== -1 && i !== activeIndex) setActive(i);
|
|
3762
|
+
});
|
|
3763
|
+
document.addEventListener('click', function(e){
|
|
3764
|
+
if (!list.hidden && e.target !== input && !list.contains(e.target)) closeList();
|
|
3765
|
+
});
|
|
3766
|
+
})();
|
|
3767
|
+
|
|
3768
|
+
/* ---------- Search (botón limpiar funcional) ---------- */
|
|
3769
|
+
(function(){
|
|
3770
|
+
var input = document.getElementById('searchInput');
|
|
3771
|
+
var clear = document.getElementById('searchClear');
|
|
3772
|
+
if (!input || !clear) return;
|
|
3773
|
+
function sync(){ clear.hidden = input.value.length === 0; }
|
|
3774
|
+
input.addEventListener('input', sync);
|
|
3775
|
+
clear.addEventListener('click', function(){ input.value = ''; sync(); input.focus(); });
|
|
3776
|
+
sync();
|
|
3777
|
+
})();
|
|
3778
|
+
|
|
3779
|
+
/* ---------- Checkbox indeterminate (solo se puede fijar por JS) ---------- */
|
|
3780
|
+
var cbInd = document.getElementById('cbIndeterminate');
|
|
3781
|
+
if (cbInd) cbInd.indeterminate = true;
|
|
3782
|
+
|
|
3783
|
+
/* ---------- Tabla: orden por columna + seleccionar todo ---------- */
|
|
3784
|
+
(function(){
|
|
3785
|
+
var table = document.getElementById('demoSortTable');
|
|
3786
|
+
if (!table) return;
|
|
3787
|
+
var tbody = table.tBodies[0];
|
|
3788
|
+
var headRow = table.tHead.rows[0];
|
|
3789
|
+
|
|
3790
|
+
function cellValue(row, idx){
|
|
3791
|
+
var cell = row.cells[idx];
|
|
3792
|
+
if (!cell) return '';
|
|
3793
|
+
var raw = cell.getAttribute('data-sort');
|
|
3794
|
+
if (raw !== null) return parseFloat(raw);
|
|
3795
|
+
if (cell.classList.contains('num')) return parseFloat(cell.textContent.replace(/[^\d.-]/g, '')) || 0;
|
|
3796
|
+
return cell.textContent.trim().toLowerCase();
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
Array.prototype.forEach.call(headRow.cells, function(th, idx){
|
|
3800
|
+
var btn = th.querySelector('.th-sort');
|
|
3801
|
+
if (!btn) return;
|
|
3802
|
+
btn.addEventListener('click', function(){
|
|
3803
|
+
var current = th.getAttribute('aria-sort');
|
|
3804
|
+
var dir = current === 'ascending' ? 'descending' : 'ascending';
|
|
3805
|
+
/* una sola columna ordenada a la vez */
|
|
3806
|
+
Array.prototype.forEach.call(headRow.cells, function(other){
|
|
3807
|
+
if (other !== th && other.hasAttribute('aria-sort')) other.setAttribute('aria-sort', 'none');
|
|
3808
|
+
});
|
|
3809
|
+
th.setAttribute('aria-sort', dir);
|
|
3810
|
+
var rows = Array.prototype.slice.call(tbody.rows);
|
|
3811
|
+
rows.sort(function(a, b){
|
|
3812
|
+
var va = cellValue(a, idx), vb = cellValue(b, idx);
|
|
3813
|
+
if (va < vb) return dir === 'ascending' ? -1 : 1;
|
|
3814
|
+
if (va > vb) return dir === 'ascending' ? 1 : -1;
|
|
3815
|
+
return 0;
|
|
3816
|
+
});
|
|
3817
|
+
rows.forEach(function(r){ tbody.appendChild(r); });
|
|
3818
|
+
});
|
|
3819
|
+
});
|
|
3820
|
+
|
|
3821
|
+
/* Seleccionar todo: refleja el estado (todas / ninguna / indeterminate) */
|
|
3822
|
+
var selectAll = document.getElementById('tblSelectAll');
|
|
3823
|
+
function rowBoxes(){ return Array.prototype.slice.call(tbody.querySelectorAll('.col-select .checkbox')); }
|
|
3824
|
+
function syncSelectAll(){
|
|
3825
|
+
if (!selectAll) return;
|
|
3826
|
+
var boxes = rowBoxes(), checked = boxes.filter(function(b){ return b.checked; }).length;
|
|
3827
|
+
selectAll.checked = checked === boxes.length && boxes.length > 0;
|
|
3828
|
+
selectAll.indeterminate = checked > 0 && checked < boxes.length;
|
|
3829
|
+
}
|
|
3830
|
+
function syncRow(box){
|
|
3831
|
+
var tr = box.closest('tr');
|
|
3832
|
+
if (tr) tr.classList.toggle('row--selected', box.checked);
|
|
3833
|
+
}
|
|
3834
|
+
if (selectAll) {
|
|
3835
|
+
selectAll.addEventListener('change', function(){
|
|
3836
|
+
rowBoxes().forEach(function(b){ b.checked = selectAll.checked; syncRow(b); });
|
|
3837
|
+
selectAll.indeterminate = false;
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
3840
|
+
rowBoxes().forEach(function(b){
|
|
3841
|
+
b.addEventListener('change', function(){ syncRow(b); syncSelectAll(); });
|
|
3842
|
+
});
|
|
3843
|
+
syncSelectAll(); /* estado inicial: una fila marcada → indeterminate */
|
|
3844
|
+
})();
|
|
3845
|
+
|
|
2584
3846
|
/* ---------- Progress demo ---------- */
|
|
2585
3847
|
var seq = [22, 47, 64, 81, 95], seqI = 2;
|
|
2586
3848
|
function paintProgressRing(v){
|
|
@@ -2708,6 +3970,128 @@ if (mReplayBtn) {
|
|
|
2708
3970
|
});
|
|
2709
3971
|
});
|
|
2710
3972
|
}
|
|
3973
|
+
|
|
3974
|
+
/* ---------- DatePicker (calendario en popover; flechas + Enter/Esc) ---------- */
|
|
3975
|
+
(function(){
|
|
3976
|
+
var wrap = document.getElementById('datepicker');
|
|
3977
|
+
var input = document.getElementById('dpInput');
|
|
3978
|
+
var cal = document.getElementById('dpCalendar');
|
|
3979
|
+
var grid = document.getElementById('dpGrid');
|
|
3980
|
+
var title = document.getElementById('dpTitle');
|
|
3981
|
+
var prev = document.getElementById('dpPrev');
|
|
3982
|
+
var next = document.getElementById('dpNext');
|
|
3983
|
+
if (!wrap || !input || !cal || !grid) return;
|
|
3984
|
+
|
|
3985
|
+
var MONTHS = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];
|
|
3986
|
+
var today = new Date(2026, 6, 10); // 10 de julio de 2026 (fecha del demo)
|
|
3987
|
+
var selected = new Date(2026, 6, 15); // día seleccionado inicial
|
|
3988
|
+
var view = new Date(2026, 6, 1); // mes visible
|
|
3989
|
+
var focusDate = new Date(selected); // día con el roving tabindex
|
|
3990
|
+
|
|
3991
|
+
function sameDay(a, b){ return a.getFullYear()===b.getFullYear() && a.getMonth()===b.getMonth() && a.getDate()===b.getDate(); }
|
|
3992
|
+
function fmtLabel(d){ return d.getDate() + ' de ' + MONTHS[d.getMonth()] + ' de ' + d.getFullYear(); }
|
|
3993
|
+
|
|
3994
|
+
function render(){
|
|
3995
|
+
var y = view.getFullYear(), m = view.getMonth();
|
|
3996
|
+
title.textContent = MONTHS[m] + ' ' + y;
|
|
3997
|
+
grid.innerHTML = '';
|
|
3998
|
+
var first = new Date(y, m, 1);
|
|
3999
|
+
var startOffset = (first.getDay() + 6) % 7; // la semana empieza en lunes
|
|
4000
|
+
var start = new Date(y, m, 1 - startOffset);
|
|
4001
|
+
for (var w = 0; w < 6; w++) {
|
|
4002
|
+
var row = document.createElement('div');
|
|
4003
|
+
row.className = 'calendar__week'; row.setAttribute('role', 'row');
|
|
4004
|
+
for (var d = 0; d < 7; d++) {
|
|
4005
|
+
var cur = new Date(start.getFullYear(), start.getMonth(), start.getDate() + (w*7 + d));
|
|
4006
|
+
var btn = document.createElement('button');
|
|
4007
|
+
btn.type = 'button';
|
|
4008
|
+
btn.className = 'calendar__day';
|
|
4009
|
+
btn.setAttribute('role', 'gridcell');
|
|
4010
|
+
btn.textContent = cur.getDate();
|
|
4011
|
+
btn.setAttribute('aria-label', fmtLabel(cur));
|
|
4012
|
+
btn.dataset.y = cur.getFullYear(); btn.dataset.m = cur.getMonth(); btn.dataset.d = cur.getDate();
|
|
4013
|
+
if (cur.getMonth() !== m) btn.classList.add('calendar__day--outside');
|
|
4014
|
+
if (sameDay(cur, today)) { btn.classList.add('calendar__day--today'); btn.setAttribute('aria-current', 'date'); }
|
|
4015
|
+
var isSel = sameDay(cur, selected);
|
|
4016
|
+
btn.setAttribute('aria-selected', isSel ? 'true' : 'false');
|
|
4017
|
+
if (isSel) btn.classList.add('calendar__day--selected');
|
|
4018
|
+
btn.tabIndex = sameDay(cur, focusDate) ? 0 : -1;
|
|
4019
|
+
row.appendChild(btn);
|
|
4020
|
+
}
|
|
4021
|
+
grid.appendChild(row);
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
function focusRoving(){ var f = grid.querySelector('.calendar__day[tabindex="0"]'); if (f) f.focus(); }
|
|
4025
|
+
function open(){ render(); cal.hidden = false; focusRoving(); }
|
|
4026
|
+
function close(returnFocus){
|
|
4027
|
+
if (cal.hidden) return;
|
|
4028
|
+
cal.hidden = true;
|
|
4029
|
+
if (returnFocus) input.focus();
|
|
4030
|
+
}
|
|
4031
|
+
function setView(y, m){ view = new Date(y, m, 1); render(); }
|
|
4032
|
+
function moveFocus(newDate){
|
|
4033
|
+
focusDate = newDate;
|
|
4034
|
+
setView(newDate.getFullYear(), newDate.getMonth());
|
|
4035
|
+
focusRoving();
|
|
4036
|
+
}
|
|
4037
|
+
function choose(y, m, d){
|
|
4038
|
+
selected = new Date(y, m, d);
|
|
4039
|
+
focusDate = new Date(selected);
|
|
4040
|
+
input.value = fmtLabel(selected);
|
|
4041
|
+
close(true);
|
|
4042
|
+
}
|
|
4043
|
+
|
|
4044
|
+
input.addEventListener('click', function(){ if (cal.hidden) open(); else close(true); });
|
|
4045
|
+
prev.addEventListener('click', function(){ focusDate = new Date(view.getFullYear(), view.getMonth() - 1, Math.min(focusDate.getDate(), 28)); setView(view.getFullYear(), view.getMonth() - 1); });
|
|
4046
|
+
next.addEventListener('click', function(){ focusDate = new Date(view.getFullYear(), view.getMonth() + 1, Math.min(focusDate.getDate(), 28)); setView(view.getFullYear(), view.getMonth() + 1); });
|
|
4047
|
+
|
|
4048
|
+
grid.addEventListener('click', function(e){
|
|
4049
|
+
var b = e.target.closest('.calendar__day');
|
|
4050
|
+
if (!b || b.disabled) return;
|
|
4051
|
+
choose(+b.dataset.y, +b.dataset.m, +b.dataset.d);
|
|
4052
|
+
});
|
|
4053
|
+
grid.addEventListener('keydown', function(e){
|
|
4054
|
+
var b = e.target.closest('.calendar__day');
|
|
4055
|
+
if (!b) return;
|
|
4056
|
+
var cur = new Date(+b.dataset.y, +b.dataset.m, +b.dataset.d), delta = 0;
|
|
4057
|
+
if (e.key === 'ArrowLeft') delta = -1;
|
|
4058
|
+
else if (e.key === 'ArrowRight') delta = 1;
|
|
4059
|
+
else if (e.key === 'ArrowUp') delta = -7;
|
|
4060
|
+
else if (e.key === 'ArrowDown') delta = 7;
|
|
4061
|
+
else if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); choose(+b.dataset.y, +b.dataset.m, +b.dataset.d); return; }
|
|
4062
|
+
else if (e.key === 'Escape') { e.preventDefault(); close(true); return; }
|
|
4063
|
+
else if (e.key === 'Home') { e.preventDefault(); moveFocus(new Date(cur.getFullYear(), cur.getMonth(), cur.getDate() - ((cur.getDay()+6)%7))); return; }
|
|
4064
|
+
else if (e.key === 'End') { e.preventDefault(); moveFocus(new Date(cur.getFullYear(), cur.getMonth(), cur.getDate() + (6 - ((cur.getDay()+6)%7)))); return; }
|
|
4065
|
+
else return;
|
|
4066
|
+
e.preventDefault();
|
|
4067
|
+
moveFocus(new Date(cur.getFullYear(), cur.getMonth(), cur.getDate() + delta));
|
|
4068
|
+
});
|
|
4069
|
+
document.addEventListener('click', function(e){
|
|
4070
|
+
if (!cal.hidden && !wrap.contains(e.target)) close(false);
|
|
4071
|
+
});
|
|
4072
|
+
|
|
4073
|
+
render(); // estado inicial coherente aunque el panel esté oculto
|
|
4074
|
+
})();
|
|
4075
|
+
|
|
4076
|
+
/* ---------- FileUpload (dragover + quitar ítem) ---------- */
|
|
4077
|
+
(function(){
|
|
4078
|
+
var fu = document.getElementById('fileUpload');
|
|
4079
|
+
var list = document.getElementById('fuList');
|
|
4080
|
+
if (!fu) return;
|
|
4081
|
+
var depth = 0; // cuenta enter/leave anidados para no parpadear con hijos
|
|
4082
|
+
fu.addEventListener('dragenter', function(e){ e.preventDefault(); depth++; fu.classList.add('is-dragover'); });
|
|
4083
|
+
fu.addEventListener('dragover', function(e){ e.preventDefault(); });
|
|
4084
|
+
fu.addEventListener('dragleave', function(){ depth--; if (depth <= 0) { depth = 0; fu.classList.remove('is-dragover'); } });
|
|
4085
|
+
fu.addEventListener('drop', function(e){ e.preventDefault(); depth = 0; fu.classList.remove('is-dragover'); });
|
|
4086
|
+
if (list) {
|
|
4087
|
+
list.addEventListener('click', function(e){
|
|
4088
|
+
var btn = e.target.closest('.file-upload__remove');
|
|
4089
|
+
if (!btn) return;
|
|
4090
|
+
var item = btn.closest('.file-upload__item');
|
|
4091
|
+
if (item) item.parentNode.removeChild(item);
|
|
4092
|
+
});
|
|
4093
|
+
}
|
|
4094
|
+
})();
|
|
2711
4095
|
})();
|
|
2712
4096
|
</script>
|
|
2713
4097
|
</body>
|