@ajuarezso/sui-design-system 0.25.0 → 0.25.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/docs/sui-accordion/README.md +83 -9
  2. package/docs/sui-activity-feed/README.md +55 -1
  3. package/docs/sui-activity-filter/README.md +50 -1
  4. package/docs/sui-alert/README.md +54 -0
  5. package/docs/sui-app-store-badge/README.md +57 -3
  6. package/docs/sui-avatar/README.md +66 -6
  7. package/docs/sui-avatar-group/README.md +64 -2
  8. package/docs/sui-badge/README.md +79 -11
  9. package/docs/sui-banner/README.md +70 -5
  10. package/docs/sui-brand-logo/README.md +77 -14
  11. package/docs/sui-breadcrumb-item/README.md +51 -0
  12. package/docs/sui-breadcrumbs/README.md +48 -0
  13. package/docs/sui-button/README.md +68 -6
  14. package/docs/sui-button-group/README.md +70 -5
  15. package/docs/sui-button-group-item/README.md +68 -4
  16. package/docs/sui-card/README.md +97 -10
  17. package/docs/sui-chart-legend/README.md +53 -0
  18. package/docs/sui-chart-legend-dot/README.md +49 -0
  19. package/docs/sui-checkbox/README.md +106 -26
  20. package/docs/sui-checkbox-card/README.md +58 -2
  21. package/docs/sui-checkbox-label/README.md +89 -23
  22. package/docs/sui-circular-progress-bar/README.md +63 -5
  23. package/docs/sui-color-dot/README.md +61 -5
  24. package/docs/sui-color-picker/README.md +62 -4
  25. package/docs/sui-command-menu-footer/README.md +49 -0
  26. package/docs/sui-command-menu-item/README.md +63 -4
  27. package/docs/sui-command-menu-search/README.md +58 -4
  28. package/docs/sui-compact-avatar-group/README.md +61 -4
  29. package/docs/sui-compact-button/README.md +112 -28
  30. package/docs/sui-compact-select/README.md +58 -4
  31. package/docs/sui-compact-select-for-input/README.md +69 -5
  32. package/docs/sui-content-card/README.md +63 -2
  33. package/docs/sui-content-divider/README.md +49 -1
  34. package/docs/sui-content-label/README.md +83 -4
  35. package/docs/sui-counter-input/README.md +91 -7
  36. package/docs/sui-date-selector/README.md +61 -3
  37. package/docs/sui-datepicker/README.md +72 -5
  38. package/docs/sui-day-cell/README.md +60 -3
  39. package/docs/sui-day-label/README.md +52 -3
  40. package/docs/sui-divider/README.md +57 -5
  41. package/docs/sui-drawer-footer/README.md +63 -2
  42. package/docs/sui-drawer-header/README.md +60 -3
  43. package/docs/sui-dropdown-caption/README.md +53 -1
  44. package/docs/sui-dropdown-item/README.md +72 -6
  45. package/docs/sui-dropdown-menu/README.md +74 -5
  46. package/docs/sui-dropdown-search/README.md +57 -1
  47. package/docs/sui-emoji/README.md +82 -18
  48. package/docs/sui-empty-state/README.md +68 -4
  49. package/docs/sui-fancy-button/README.md +59 -1
  50. package/docs/sui-file-icon/README.md +56 -1
  51. package/docs/sui-file-upload/README.md +69 -9
  52. package/docs/sui-file-upload-card/README.md +53 -2
  53. package/docs/sui-filter/README.md +74 -5
  54. package/docs/sui-flag/README.md +62 -1
  55. package/docs/sui-form-hint/README.md +54 -5
  56. package/docs/sui-form-label/README.md +53 -1
  57. package/docs/sui-href/README.md +47 -0
  58. package/docs/sui-icon/README.md +69 -4
  59. package/docs/sui-image-upload/README.md +70 -3
  60. package/docs/sui-image-upload-profile/README.md +58 -0
  61. package/docs/sui-img-retry/README.md +49 -0
  62. package/docs/sui-inline-input/README.md +61 -3
  63. package/docs/sui-inline-select/README.md +60 -0
  64. package/docs/sui-input/README.md +77 -14
  65. package/docs/sui-integration-switch/README.md +69 -2
  66. package/docs/sui-kbd/README.md +71 -7
  67. package/docs/sui-key-icon/README.md +65 -6
  68. package/docs/sui-link-button/README.md +79 -13
  69. package/docs/sui-logo/README.md +74 -4
  70. package/docs/sui-modal/README.md +61 -2
  71. package/docs/sui-modal-footer/README.md +83 -9
  72. package/docs/sui-modal-header/README.md +56 -0
  73. package/docs/sui-modal-sheet/README.md +72 -10
  74. package/docs/sui-notification/README.md +71 -1
  75. package/docs/sui-notification-footer/README.md +51 -1
  76. package/docs/sui-notification-tab-menu/README.md +57 -1
  77. package/docs/sui-otp-input/README.md +67 -6
  78. package/docs/sui-page-header/README.md +83 -15
  79. package/docs/sui-pagination/README.md +60 -0
  80. package/docs/sui-password-strength/README.md +50 -1
  81. package/docs/sui-payment-icon/README.md +66 -7
  82. package/docs/sui-period-range/README.md +65 -6
  83. package/docs/sui-popover/README.md +73 -7
  84. package/docs/sui-product-card/README.md +76 -8
  85. package/docs/sui-progress-bar/README.md +72 -8
  86. package/docs/sui-progress-bar-label/README.md +69 -12
  87. package/docs/sui-radio/README.md +83 -6
  88. package/docs/sui-radio-card/README.md +58 -4
  89. package/docs/sui-radio-label/README.md +86 -19
  90. package/docs/sui-rating/README.md +81 -14
  91. package/docs/sui-rich-editor/README.md +45 -0
  92. package/docs/sui-segmented-control/README.md +83 -14
  93. package/docs/sui-select/README.md +72 -3
  94. package/docs/sui-sidebar/README.md +64 -9
  95. package/docs/sui-slider/README.md +68 -1
  96. package/docs/sui-social-button/README.md +63 -3
  97. package/docs/sui-status-badge/README.md +64 -6
  98. package/docs/sui-stepper/README.md +57 -0
  99. package/docs/sui-store-card/README.md +71 -8
  100. package/docs/sui-switch/README.md +80 -9
  101. package/docs/sui-switch-card/README.md +70 -6
  102. package/docs/sui-switch-label/README.md +90 -13
  103. package/docs/sui-tab-chips/README.md +79 -9
  104. package/docs/sui-tab-menu-horizontal/README.md +62 -1
  105. package/docs/sui-tab-menu-vertical/README.md +56 -3
  106. package/docs/sui-table/README.md +68 -7
  107. package/docs/sui-tag/README.md +75 -4
  108. package/docs/sui-tag-input/README.md +71 -7
  109. package/docs/sui-textarea/README.md +78 -11
  110. package/docs/sui-time-picker/README.md +70 -7
  111. package/docs/sui-toast/README.md +65 -5
  112. package/docs/sui-tooltip/README.md +61 -4
  113. package/docs/sui-topbar/README.md +57 -3
  114. package/docs/sui-topbar-button/README.md +57 -3
  115. package/docs/sui-topbar-item/README.md +61 -0
  116. package/docs/sui-topbar-user/README.md +56 -0
  117. package/fesm2022/ajuarezso-sui-design-system.mjs +216 -227
  118. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  119. package/package.json +1 -1
  120. package/types/ajuarezso-sui-design-system.d.ts +68 -45
@@ -79,10 +79,12 @@ pocas horas → select.**
79
79
  | `disabled` | Mientras se guarda (como hacen el horario y los turnos). | canon (aprobado por el dueño, 05-10-2026, bloque B8) |
80
80
  | `placeholder` | El default «Elige una hora» sirve. Si hace falta decir qué hora, en imperativo con tuteo: «Elige la hora de cierre». | canon (dueño, 05-10-2026, P4 a); el default cambió en la 0.25.0 (antes «Seleccionar hora») |
81
81
 
82
- Usos de hoy que rompen la regla: los 7 cumplen `12h`, `durationPresets=[]` y `label`, pero **los 7
83
- van con `size="medium"` fijo**, también en pantallas del teléfono (turnos y votaciones del
84
- repartidor): campo de 40, contra el `large` de la tabla de «Tamaño». Cancelar / Aplicar ya salen en
85
- 48 en el teléfono desde el 04-10-2026 (el componente los fuerza en mobile).
82
+ Usos de hoy que rompen la regla (medido de nuevo el 06-10-2026): los 7 cumplen `12h`,
83
+ `durationPresets=[]` y `label`, y ya no van en `medium` fijo, pero **los 7 van con
84
+ `bp.isMobile() ? 'large' : 'medium'`** en vez de `bp.sizeControl()`: en la tablet táctil el campo
85
+ queda en 40 mientras el botón de al lado ya sube a 48 (p. ej.
86
+ `pages/driver/driver-dashboard/driver-dashboard.html:1521` junto a «Pedir cierre»). Cancelar /
87
+ Aplicar ya salen en 48 en el teléfono desde el 04-10-2026 (el componente los fuerza en mobile).
86
88
 
87
89
  ### Las piezas que trae (exportadas, sin carpeta ni story propias)
88
90
 
@@ -120,10 +122,28 @@ campo y la de las filas van en 16/24; la fila sigue en 44 (el padding bajó de 1
120
122
 
121
123
  ```html
122
124
  <sui-time-picker label="Cierra" format="12h" [step]="5" [durationPresets]="[]"
123
- [size]="bp.isMobile() ? 'large' : 'medium'"
125
+ [size]="bp.sizeControl()"
124
126
  [value]="cierre()" (valueChange)="cierre.set($event)" />
125
127
  ```
126
128
 
129
+ ### Por dispositivo (D16)
130
+
131
+ Fila `time-picker` de la tabla D16 (dueño, 05-10-2026), la misma de `sui-input` y `sui-select`: se
132
+ toca, así que va por tipo de puntero además del ancho, y mide lo mismo que los campos y el botón
133
+ que tiene al lado.
134
+
135
+ | dispositivo | size | nota |
136
+ |---|---|---|
137
+ | celular chico (< 380 px) | `large` (48) | D16 pide `medium` con área tocable de 44; mientras el DS no estire el área de los `medium`, va `large` (interino) |
138
+ | celular (380–767 px) | `large` (48) | `xlarge` (52) solo si va junto a campos `xlarge` |
139
+ | tablet táctil (`pointer: coarse`) | `large` (48) | con `large` el panel también crece (filas de 44, Cancelar / Aplicar en 48); el arreglo automático del pie solo mira el ancho (≤ 767 px), así que en la tablet hace falta pasar `large` |
140
+ | computadora (`pointer: fine`) | `medium` (40) | también en filas densas; `small` no se usa |
141
+
142
+ Ultrawide (≥ 1920 px): igual que la computadora, `medium`; el espacio extra va a ancho y columnas,
143
+ no a campos más altos.
144
+
145
+ En master-pedidos: `[size]="bp.sizeControl()"`.
146
+
127
147
  ## «No usar»: por qué no hoy y cuándo sí
128
148
 
129
149
  Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
@@ -234,13 +254,56 @@ interface SuiTimeDurationPreset { label: string; minutes: number; }
234
254
  defecto (antes venían en inglés del Figma); `label` sigue sirviendo para cambiarlo.
235
255
  - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador).
236
256
 
257
+ ## Bien y mal
258
+
259
+ Hora junto a un botón
260
+
261
+ ```html
262
+ <!-- ❌ mal: bp.isMobile() deja el campo en medium (40) en la tablet táctil, y el botón de al lado con bp.sizeControl() sube a large (48) (pages/driver/driver-dashboard/driver-dashboard.html:1521) -->
263
+ <sui-time-picker [size]="bp.isMobile() ? 'large' : 'medium'" format="12h" [step]="5" [durationPresets]="[]"
264
+ label="Cerrar hasta" [value]="horaCierreProp()" (valueChange)="onHoraCierreChange($event)" />
265
+ ```
266
+
267
+ ```html
268
+ <!-- ✅ bien -->
269
+ <sui-time-picker [size]="bp.sizeControl()" format="12h" [step]="5" [durationPresets]="[]"
270
+ label="Cerrar hasta" [value]="horaCierreProp()" (valueChange)="onHoraCierreChange($event)" />
271
+ ```
272
+
273
+ Presets de duración
274
+
275
+ ```html
276
+ <!-- ❌ mal: los presets no suman sobre la hora: «30 min» deja 12:30 AM (ver Trampas) -->
277
+ <sui-time-picker label="Cerrar por" [durationPresets]="[{ label: '30 min', minutes: 30 }, { label: '1 h', minutes: 60 }]"
278
+ [value]="hasta()" (valueChange)="hasta.set($event)" />
279
+ ```
280
+
281
+ ```html
282
+ <!-- ✅ bien: se pide la hora del día, sin presets -->
283
+ <sui-time-picker label="Cerrar hasta" format="12h" [step]="5" [durationPresets]="[]" [size]="bp.sizeControl()"
284
+ [value]="hasta()" (valueChange)="hasta.set($event)" />
285
+ ```
286
+
287
+ Formato y paso
288
+
289
+ ```html
290
+ <!-- ❌ mal: en Perú se dice «9:30 PM», y 60 minutos en la columna para una precisión que nadie pide -->
291
+ <sui-time-picker label="Abre" format="24h" [step]="1" [value]="apertura()" (valueChange)="apertura.set($event)" />
292
+ ```
293
+
294
+ ```html
295
+ <!-- ✅ bien -->
296
+ <sui-time-picker label="Abre" format="12h" [step]="5" [durationPresets]="[]" [size]="bp.sizeControl()"
297
+ [value]="apertura()" (valueChange)="apertura.set($event)" />
298
+ ```
299
+
237
300
  ## Ejemplo
238
301
 
239
302
  ```html
240
303
  <!-- Horario del local: abre / cierra -->
241
- <sui-time-picker label="Abre" format="12h" [step]="5" [durationPresets]="[]"
304
+ <sui-time-picker label="Abre" format="12h" [step]="5" [durationPresets]="[]" [size]="bp.sizeControl()"
242
305
  [value]="apertura()" (valueChange)="apertura.set($event)" />
243
- <sui-time-picker label="Cierra" format="12h" [step]="5" [durationPresets]="[]"
306
+ <sui-time-picker label="Cierra" format="12h" [step]="5" [durationPresets]="[]" [size]="bp.sizeControl()"
244
307
  [error]="cierraAntes()" errorMessage="Cierra antes de abrir"
245
308
  [value]="cierre()" (valueChange)="cierre.set($event)" />
246
309
  ```
@@ -101,14 +101,15 @@ arma el título con variables. La pasada de copy del 03-10-2026 en la app ya cor
101
101
  `duration: Infinity` de «Acerca de» (ahora duran 6 s). Queda:
102
102
 
103
103
  - **Rompe el canon de copy — 1 error que empieza con «Error…»:**
104
- `pages/driver/driver-order-detail/driver-order-detail.ts:1548` («Error al cancelar el pedido»).
104
+ `pages/driver/driver-order-detail/driver-order-detail.ts:1647` («Error al cancelar el pedido»; línea al 06-10-2026).
105
105
  - **Rompen el canon de copy — 18 errores que muestran `e.message`** cuando existe (puede salir
106
- texto técnico o en inglés): `components/transfer-tenant-modal/transfer-tenant-modal.ts:82`,
107
- `pages/driver/driver-dashboard/driver-dashboard.ts:1601`, `pages/central/central-gastos/central-gastos.ts:183`.
108
- Algunos de esos textos de respaldo además llevan punto final (`pages/super-admin/master-viajes/master-viajes.ts:82`).
106
+ texto técnico o en inglés): `components/transfer-tenant-modal/transfer-tenant-modal.ts:84`,
107
+ `pages/driver/driver-dashboard/driver-dashboard.ts:1706`, `pages/central/central-gastos/central-gastos.ts:185`
108
+ (y `:205`). Algunos de esos textos de respaldo además llevan punto final
109
+ (`pages/super-admin/master-viajes/master-viajes.ts:243`, «No se pudo habilitar.»; líneas al 06-10-2026).
109
110
  - **Romperían la propuesta — 57 errores al cargar como toast** («No se pudo cargar…») → alert o
110
111
  empty state con «Reintentar»: `pages/driver/driver-history/driver-history.ts:200`,
111
- `pages/settings/team/team.ts:307`.
112
+ `pages/settings/team/team.ts:308`.
112
113
 
113
114
  ## Tamaño en mobile y en web
114
115
 
@@ -124,6 +125,26 @@ El toast **no tiene `size`**: siempre es un alert `large`.
124
125
  No hay patrón `bp.isMobile()`: el outlet ya cambia solo por breakpoint. Lo que sí cambia por
125
126
  plataforma es el largo del texto: en mobile el título envuelve antes, así que una sola idea por toast.
126
127
 
128
+ ### Por dispositivo (D16)
129
+
130
+ El toast no tiene `size` (siempre es un alert `large`): no hay size que elegir por dispositivo ni
131
+ nada que pasar desde la app (en master-pedidos no lleva `bp`). Ancho y posición los resuelve el
132
+ outlet solo, por ancho, y está bien así.
133
+
134
+ Lo que **sí** queda corto es la X de cerrar, que se toca:
135
+
136
+ | dispositivo | área de la X hoy | D16 pide |
137
+ |---|---|---|
138
+ | celular chico y celular (≤ 767 px) | 44 × 44 | 44 × 44 (cumple) |
139
+ | tablet táctil (`pointer: coarse`, más de 767 px) | **40 × 40** | 44 × 44 |
140
+ | computadora (`pointer: fine`) | 40 × 40 | sin piso de 44 (cumple) |
141
+
142
+ **Pendiente del DS**, no de la app: el área de 44 se prende con `@media (max-width: 767px)`
143
+ (`sui-toast-outlet.scss:182`), así que el iPad apaisado y la tablet ancha se quedan en 40. Se
144
+ arregla con el mixin `sui-tactil` (`@media (max-width: 767px), (pointer: coarse)`) que propone la
145
+ sección 0 de las decisiones D16; mientras no exista, no hay nada que la app pueda pasar para
146
+ corregirlo.
147
+
127
148
  ## «No usar»: por qué no hoy y cuándo sí
128
149
 
129
150
  Nada se borra del DS. Si un flujo nuevo cae en la columna de la derecha, se usa.
@@ -173,6 +194,45 @@ Outputs: ninguno.
173
194
  - El título es texto plano; la `description` del alert interno va por `innerHTML`: no le pases
174
195
  texto escrito por el usuario sin sanear.
175
196
 
197
+ ## Bien y mal
198
+
199
+ El copy del error
200
+
201
+ ```ts
202
+ // ❌ mal: «Error al…» en vez de decir qué no se pudo (pages/driver/driver-order-detail/driver-order-detail.ts:1647)
203
+ this.toast.error('Error al cancelar el pedido');
204
+ ```
205
+
206
+ ```ts
207
+ // ✅ bien
208
+ this.toast.error('No se pudo cancelar el pedido');
209
+ ```
210
+
211
+ El mensaje técnico en el título
212
+
213
+ ```ts
214
+ // ❌ mal: e.message puede ser técnico o en inglés (pages/driver/driver-dashboard/driver-dashboard.ts:1706)
215
+ this.toast.error((e instanceof Error && e.message) || 'No se pudo abrir la votación');
216
+ ```
217
+
218
+ ```ts
219
+ // ✅ bien: el detalle va a la consola; la persona lee qué no se pudo
220
+ console.error('[votación]', e);
221
+ this.toast.error('No se pudo abrir la votación');
222
+ ```
223
+
224
+ Un error al cargar la pantalla (sigue en propuesta: ver «Usos de hoy que rompen la regla»)
225
+
226
+ ```ts
227
+ // ❌ mal: el toast se va y deja la lista vacía sin explicación (pages/driver/driver-history/driver-history.ts:200)
228
+ this.toast.error('No se pudo cargar el historial');
229
+ ```
230
+
231
+ ```ts
232
+ // ✅ bien: la pantalla queda con un sui-alert o un sui-empty-state con «Reintentar»
233
+ this.errorAlCargar.set(true);
234
+ ```
235
+
176
236
  ## Ejemplo
177
237
 
178
238
  ```ts
@@ -96,17 +96,34 @@ dos `<svg>` con tooltip del panel del local pasaron a subtítulo
96
96
  | frase que explica | no aplica: el texto va visible bajo el dato (`sui-form-hint`) | `medium` | canon (aprobado por el dueño, 05-10-2026, bloque B7) |
97
97
  | explicación con título | no aplica: `sui-modal-sheet` al tocar el ícono | `large` (automático con `[suiTooltipLarge]`) | canon (aprobado por el dueño, 05-10-2026, bloque B7) |
98
98
 
99
- En pantallas que se usan en los dos (panel del local, super admin), el tooltip se apaga en mobile
100
- y la ayuda se muestra de otra forma:
99
+ En pantallas que se usan en los dos (panel del local, super admin), el tooltip se apaga en el
100
+ celular y en la tablet táctil, y la ayuda se muestra de otra forma:
101
101
 
102
102
  ```html
103
103
  <button class="info" type="button" aria-label="Qué es la tarifa de servicio"
104
- [suiTooltipLarge]="tarifa" theme="auto" [disabled]="bp.isMobile()"
105
- (click)="bp.isMobile() && abrirExplicacion()">
104
+ [suiTooltipLarge]="tarifa" theme="auto" [disabled]="bp.tactil()"
105
+ (click)="bp.tactil() && abrirExplicacion()">
106
106
  <sui-icon name="information-line" [size]="16" />
107
107
  </button>
108
108
  ```
109
109
 
110
+ ### Por dispositivo (D16)
111
+
112
+ Fila `tooltip` de la tabla D16 (dueño, 05-10-2026). Solo existe donde hay mouse: con el dedo no hay
113
+ hover, y eso incluye la tablet aunque sea ancha.
114
+
115
+ | dispositivo | size | nota |
116
+ |---|---|---|
117
+ | celular chico (< 380 px) | no aplica | sin hover: texto visible o un `sui-modal-sheet` al tocar |
118
+ | celular (380–767 px) | no aplica | igual que el celular chico |
119
+ | tablet táctil (`pointer: coarse`) | no aplica | también sin hover; `bp.isMobile()` no la apaga (mide más de 767), `bp.tactil()` sí |
120
+ | computadora (`pointer: fine`) | `medium` (default) | también el nombre de un ícono; `large` sale solo con `[suiTooltipLarge]`; `small` no se usa |
121
+
122
+ Ultrawide (≥ 1920 px): igual que la computadora.
123
+
124
+ En master-pedidos: sin helper de tamaño (`medium` es el default, no se pasa `suiTooltipSize`); se
125
+ apaga con `[disabled]="bp.tactil()"` y la ayuda va por otro lado.
126
+
110
127
  ## «No usar»: por qué no hoy y cuándo sí
111
128
 
112
129
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -184,6 +201,46 @@ no se usa directo en pantallas.
184
201
  gris del navegador).
185
202
  - En `dark`, el ícono del tooltip rico usa un gris fijo del `.scss`, no un token.
186
203
 
204
+ ## Bien y mal
205
+
206
+ Tooltip sobre un ícono suelto
207
+
208
+ ```html
209
+ <!-- ❌ mal: un <svg> no se enfoca (con teclado nunca aparece) y en el celular no hay hover (components/customer-segments/customer-segments.html, antes de 77ac547a) -->
210
+ <svg suiTooltip="Ingresos por tipo de cliente" position="top" width="16" height="16" viewBox="0 0 16 16">…</svg>
211
+ ```
212
+
213
+ ```html
214
+ <!-- ✅ bien: si es una aclaración del título, va escrita -->
215
+ <p class="customer-segments__subtitle">Ingresos por tipo de cliente</p>
216
+ ```
217
+
218
+ Explicación en una pantalla del cliente
219
+
220
+ ```html
221
+ <!-- ❌ mal: el carrito se usa en el celular, sin hover; la tarifa queda sin explicar (pages/customer/cart/cart.html, antes de 77ac547a) -->
222
+ <button type="button" aria-label="Qué es la tarifa de servicio" [suiTooltipLarge]="serviceFeeTooltip" theme="dark">
223
+ <sui-icon name="information-line" [size]="14" />
224
+ </button>
225
+ ```
226
+
227
+ ```html
228
+ <!-- ✅ bien: la explicación va visible bajo el dato -->
229
+ <span class="cart__fee-hint">{{ serviceFeeExplicacion }}</span>
230
+ ```
231
+
232
+ Tamaño rico con texto plano
233
+
234
+ ```html
235
+ <!-- ❌ mal: suiTooltipSize="large" solo lee suiTooltipLarge: con texto dibuja una caja vacía (ver Trampas) -->
236
+ <sui-compact-button icon="download-2-line" ariaLabel="Exportar" suiTooltip="Exportar" suiTooltipSize="large" theme="auto" />
237
+ ```
238
+
239
+ ```html
240
+ <!-- ✅ bien: el nombre de un ícono va en medium, el default -->
241
+ <sui-compact-button icon="download-2-line" ariaLabel="Exportar" suiTooltip="Exportar" theme="auto" />
242
+ ```
243
+
187
244
  ## Ejemplo
188
245
 
189
246
  ```html
@@ -21,7 +21,7 @@ Compone las tres piezas de la familia (`sui-topbar-item`, `sui-topbar-button`,
21
21
  - La navegación de los roles de la app (empresa, cliente, repartidor, super admin): es
22
22
  `sui-sidebar`, y en el teléfono la barra inferior. Hoy la topbar tiene **0 usos** en la app.
23
23
  - El título de una pantalla con sus acciones: `sui-page-header`.
24
- - Nada que se vea en un teléfono: el componente no tiene versión mobile (ver «Tamaño»).
24
+ - Nada que se vea en un celular o una tablet táctil: el componente no tiene versión mobile (ver «Tamaño»).
25
25
 
26
26
  Regla corta: **navegación de la app → sidebar · título de pantalla → page header · web de escritorio
27
27
  navegada por arriba → topbar.**
@@ -60,9 +60,22 @@ oculto si no entra.
60
60
  **Qué pasa en mobile:** nada se adapta. Los 44 px laterales se comen el ancho, los items se
61
61
  esconden detrás de un scroll lateral sin pista visual, el buscador no se achica y no hay
62
62
  `safe-area-inset-top` (en la app nativa queda debajo del notch). En mobile se usa el patrón de la
63
- app: barra inferior + cajón `sui-sidebar`. Si algún día hace falta topbar en el teléfono, primero
63
+ app: barra inferior + cajón `sui-sidebar`. Si algún día hace falta topbar en celular o tablet táctil, primero
64
64
  hay que agregarle al componente un modo mobile (menú hamburguesa, buscador en botón, safe area).
65
65
 
66
+ ### Por dispositivo (D16)
67
+
68
+ No aplica una tabla: no tiene input `size` y sus piezas tienen tamaño fijo (items de 36, íconos y
69
+ chip de 40, buscador de 40). Como se toca y no tiene un size que llegue a 44, B7 lo saca de todo lo
70
+ táctil: en el celular chico, el celular y la tablet táctil **no se usa**; solo en la computadora
71
+ (`pointer: fine`) y en el ultrawide, igual que en la computadora, con el ancho extra para el
72
+ contenido y no para la barra. La columna «mobile» de D16 incluye la tablet en lo que se toca, así
73
+ que el «no usar en mobile» de B7 la cubre.
74
+
75
+ En master-pedidos: sin `[size]` ni helper; si algún día se monta, va detrás de `@if (!bp.tactil())`
76
+ (`isMobile() || toque()`: todo celular por ancho y toda pantalla táctil). No `!bp.isMobile()`: ese
77
+ es solo por ancho (≤ 768) y deja pasar un iPad de 769 a 1024 con `pointer: coarse`.
78
+
66
79
  ## «No usar»: por qué no hoy y cuándo sí
67
80
 
68
81
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -129,6 +142,47 @@ Slot: `<ng-content select="[topbar-logo]">` — el logo (una `<img>` directa se
129
142
  - El chip de usuario no abre ningún menú: emite `(userClicked)` y solo cambia de aspecto con
130
143
  `userOpen`.
131
144
  - El host lleva `role="banner"`: una sola por página.
145
+ - `count` en `0` **se muestra** («0» rojo), igual que en `sui-topbar-item`: pasa
146
+ `pendientes() || undefined`.
147
+
148
+ ## Bien y mal
149
+
150
+ El contador de un item
151
+
152
+ ```html
153
+ <!-- ❌ mal: sin pendientes sale un «0» rojo -->
154
+ <sui-topbar [items]="[{ value: 'pedidos', label: 'Pedidos', count: pendientes() }]" />
155
+ ```
156
+
157
+ ```html
158
+ <!-- ✅ bien -->
159
+ <sui-topbar [items]="[{ value: 'pedidos', label: 'Pedidos', count: pendientes() || undefined }]" />
160
+ ```
161
+
162
+ Las acciones de ícono
163
+
164
+ ```html
165
+ <!-- ❌ mal: sin ariaLabel, el lector de pantalla lee el value («notifications») -->
166
+ <sui-topbar [actions]="[{ value: 'notifications', icon: 'notification-3-line', dot: hayAvisos() }]" />
167
+ ```
168
+
169
+ ```html
170
+ <!-- ✅ bien -->
171
+ <sui-topbar [actions]="[{ value: 'avisos', icon: 'notification-3-line',
172
+ ariaLabel: 'Avisos', dot: hayAvisos() }]" />
173
+ ```
174
+
175
+ El item activo en una ruta con id
176
+
177
+ ```html
178
+ <!-- ❌ mal: en /pedidos/123 la url nunca es igual a 'pedidos'; no se marca ninguno -->
179
+ <sui-topbar [items]="items" [activeValue]="router.url" (itemClicked)="ir($event.value)" />
180
+ ```
181
+
182
+ ```html
183
+ <!-- ✅ bien: seccion() saca 'pedidos' del primer tramo de la ruta -->
184
+ <sui-topbar [items]="items" [activeValue]="seccion()" (itemClicked)="ir($event.value)" />
185
+ ```
132
186
 
133
187
  ## Ejemplo
134
188
 
@@ -136,7 +190,7 @@ Slot: `<ng-content select="[topbar-logo]">` — el logo (una `<img>` directa se
136
190
  <sui-topbar
137
191
  [items]="[
138
192
  { value: 'tablero', label: 'Tablero' },
139
- { value: 'pedidos', label: 'Pedidos', count: pendientes() },
193
+ { value: 'pedidos', label: 'Pedidos', count: pendientes() || undefined },
140
194
  { value: 'mas', label: 'Más', rightIcon: 'arrow-down-s-line' }
141
195
  ]"
142
196
  [activeValue]="seccion()"
@@ -37,14 +37,26 @@ armada a mano que quiera verse como la topbar.
37
37
 
38
38
  ## Tamaño en mobile y en web
39
39
 
40
- **No tiene `size`**: mide siempre 40×40 (padding 10 + ícono de 20), radio 10. En mobile 40 es el
41
- mínimo táctil aceptable, pero el botón vive en la topbar, que hoy es solo de escritorio (ver
42
- `sui-topbar`).
40
+ **No tiene `size`**: mide siempre 40×40 (padding 10 + ícono de 20), radio 10. 40 queda debajo de
41
+ los 44 que D1 y D16 piden para lo que se toca, y por eso no va en mobile (B7): vive en la topbar,
42
+ que es solo de computadora (ver `sui-topbar`).
43
43
 
44
44
  | uso | mobile | web | fuente |
45
45
  |---|---|---|---|
46
46
  | ícono de la barra superior | no usar (no hay topbar en mobile) | 40×40 | canon (aprobado por el dueño, 05-10-2026, bloque B7) |
47
47
 
48
+ ### Por dispositivo (D16)
49
+
50
+ No aplica una tabla: no tiene input `size` y mide 40×40 en todos los dispositivos. Como se toca y no
51
+ tiene un size que llegue a 44, B7 lo saca de todo lo táctil: en el celular chico, el celular y la
52
+ tablet táctil **no se usa**; solo en la computadora (`pointer: fine`) y en el ultrawide, igual que
53
+ en la computadora. La columna «mobile» de D16 incluye la tablet en lo que se toca, así que el «no
54
+ usar en mobile» de B7 la cubre.
55
+
56
+ En master-pedidos: sin `[size]` ni helper; si algún día se monta, va detrás de `@if (!bp.tactil())`
57
+ (`isMobile() || toque()`: todo celular por ancho y toda pantalla táctil). No `!bp.isMobile()`: ese
58
+ es solo por ancho (≤ 768) y deja pasar un iPad de 769 a 1024 con `pointer: coarse`.
59
+
48
60
  ## «No usar»: por qué no hoy y cuándo sí
49
61
 
50
62
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -83,6 +95,48 @@ mínimo táctil aceptable, pero el botón vive en la topbar, que hoy es solo de
83
95
  de pantalla. Si importa, dilo en `ariaLabel` («Avisos, hay nuevos»).
84
96
  - Ya trae el arreglo de iOS para el `:active` pegado.
85
97
 
98
+ ## Bien y mal
99
+
100
+ El ícono de avisos
101
+
102
+ ```html
103
+ <!-- ❌ mal: el lector de pantalla dice «notifications» y no se entera del punto rojo -->
104
+ <sui-topbar-button icon="notification-3-line" ariaLabel="notifications" [dot]="hayAvisos()" />
105
+ ```
106
+
107
+ ```html
108
+ <!-- ✅ bien -->
109
+ <sui-topbar-button icon="notification-3-line" [dot]="hayAvisos()"
110
+ [ariaLabel]="hayAvisos() ? 'Avisos, hay nuevos' : 'Avisos'" />
111
+ ```
112
+
113
+ El panel abierto
114
+
115
+ ```html
116
+ <!-- ❌ mal: azul del Figma original y (click) en vez de la API -->
117
+ <sui-topbar-button icon="question-line" ariaLabel="Ayuda" accent="information"
118
+ [active]="panelAyuda()" (click)="panelAyuda.set(!panelAyuda())" />
119
+ ```
120
+
121
+ ```html
122
+ <!-- ✅ bien -->
123
+ <sui-topbar-button icon="question-line" ariaLabel="Ayuda"
124
+ [active]="panelAyuda()" (clicked)="panelAyuda.set(!panelAyuda())" />
125
+ ```
126
+
127
+ Un ícono en una card
128
+
129
+ ```html
130
+ <!-- ❌ mal: es el botón de la barra superior; en una card desentona (radio 10, 40 fijo) -->
131
+ <sui-topbar-button icon="more-2-line" ariaLabel="Opciones" (clicked)="abrirMenu()" />
132
+ ```
133
+
134
+ ```html
135
+ <!-- ✅ bien -->
136
+ <sui-button variant="ghost" color="neutral" [size]="bp.sizeControl()" [onlyIcon]="true"
137
+ leftIcon="more-2-line" aria-label="Opciones" (click)="abrirMenu()" />
138
+ ```
139
+
86
140
  ## Ejemplo
87
141
 
88
142
  ```html
@@ -51,6 +51,26 @@ topbar, que hoy es solo de escritorio.
51
51
  En mobile, 36 de alto queda por debajo del área táctil cómoda (44): si algún día se usa ahí, la
52
52
  barra tendría que darle más aire.
53
53
 
54
+ ### Por dispositivo (D16)
55
+
56
+ No aplica una tabla: no tiene input `size` y mide 36 de alto en todos los dispositivos. Como se toca
57
+ y no tiene un size que llegue a 44, B7 lo saca de todo lo táctil: en el celular chico, el celular y
58
+ la tablet táctil **no se usa**; solo en la computadora (`pointer: fine`) y en el ultrawide, igual
59
+ que en la computadora. La columna «mobile» de D16 incluye la tablet en lo que se toca, así que el
60
+ «no usar en mobile» de B7 la cubre.
61
+
62
+ Badges internos: el `count` va en `small` fijo (hardcodeado en el template, no se puede cambiar). El
63
+ `badge` cae en `small` cuando no se le pasa `size` (el shorthand string siempre); con
64
+ `SuiBadgeConfig` sí acepta `size`, y en la computadora va `medium`, que es lo que D16 da a lo que no
65
+ se toca (lo mismo que devuelve `bp.sizeBadge()` fuera del celular):
66
+ `[badge]="{ label: 'Nuevo', size: 'medium' }"`. Los dos `small` chocan con «nada en `small`» de D16:
67
+ cambiar el default del badge y el `count` fijo es código del DS, **pendiente** («internos del DS en
68
+ chico» en el doc de decisiones).
69
+
70
+ En master-pedidos: sin `[size]` ni helper; si algún día se monta, va detrás de `@if (!bp.tactil())`
71
+ (`isMobile() || toque()`: todo celular por ancho y toda pantalla táctil). No `!bp.isMobile()`: ese
72
+ es solo por ancho (≤ 768) y deja pasar un iPad de 769 a 1024 con `pointer: coarse`.
73
+
54
74
  ## API
55
75
 
56
76
  <!-- api:inicio -->
@@ -85,6 +105,47 @@ barra tendría que darle más aire.
85
105
  - `accent="information"` es el azul del Figma original: en esta app no se usa.
86
106
  - Ya trae el arreglo de iOS para el `:active` pegado.
87
107
 
108
+ ## Bien y mal
109
+
110
+ Pedidos por aceptar
111
+
112
+ ```html
113
+ <!-- ❌ mal: sin pendientes sale un «0» rojo; solo null lo oculta -->
114
+ <sui-topbar-item label="Pedidos" [count]="pendientes()" (clicked)="ir('pedidos')" />
115
+ ```
116
+
117
+ ```html
118
+ <!-- ✅ bien -->
119
+ <sui-topbar-item label="Pedidos" [count]="pendientes() || null" (clicked)="ir('pedidos')" />
120
+ ```
121
+
122
+ Un item que es link
123
+
124
+ ```html
125
+ <!-- ❌ mal: con href el item ya navega y (clicked) navega otra vez -->
126
+ <sui-topbar-item label="Reportes" href="/reportes" [active]="seccion() === 'reportes'"
127
+ (clicked)="router.navigateByUrl('/reportes')" />
128
+ ```
129
+
130
+ ```html
131
+ <!-- ✅ bien -->
132
+ <sui-topbar-item label="Reportes" href="/reportes" [active]="seccion() === 'reportes'" />
133
+ ```
134
+
135
+ Íconos en la barra
136
+
137
+ ```html
138
+ <!-- ❌ mal: unos con ícono y otros sin; se lee desordenado -->
139
+ <sui-topbar-item label="Tablero" icon="layout-grid-line" [active]="true" />
140
+ <sui-topbar-item label="Pedidos" />
141
+ ```
142
+
143
+ ```html
144
+ <!-- ✅ bien: todos sin ícono (el caso normal), o todos con -->
145
+ <sui-topbar-item label="Tablero" [active]="true" />
146
+ <sui-topbar-item label="Pedidos" />
147
+ ```
148
+
88
149
  ## Ejemplo
89
150
 
90
151
  ```html
@@ -44,6 +44,19 @@ topbar, que hoy es solo de escritorio.
44
44
  |---|---|---|---|
45
45
  | cuenta en la barra superior | no usar (en mobile la cuenta va en el cajón del sidebar) | con nombre | canon (aprobado por el dueño, 05-10-2026, bloque B7) |
46
46
 
47
+ ### Por dispositivo (D16)
48
+
49
+ No aplica una tabla: no tiene input `size`; mide 40 de alto con el avatar en `md` (32) fijo, en
50
+ todos los dispositivos. Como se toca y no tiene un size que llegue a 44, B7 lo saca de todo lo
51
+ táctil: en el celular chico, el celular y la tablet táctil **no se usa** (la cuenta va en el cajón
52
+ del sidebar); solo en la computadora (`pointer: fine`) y en el ultrawide, igual que en la
53
+ computadora. La columna «mobile» de D16 incluye la tablet en lo que se toca, así que el «no usar en
54
+ mobile» de B7 la cubre. El avatar no se pasa por `bp.sizeAvatar()`.
55
+
56
+ En master-pedidos: sin `[size]` ni helper; si algún día se monta, va detrás de `@if (!bp.tactil())`
57
+ (`isMobile() || toque()`: todo celular por ancho y toda pantalla táctil). No `!bp.isMobile()`: ese
58
+ es solo por ancho (≤ 768) y deja pasar un iPad de 769 a 1024 con `pointer: coarse`.
59
+
47
60
  ## API
48
61
 
49
62
  <!-- api:inicio -->
@@ -72,6 +85,49 @@ topbar, que hoy es solo de escritorio.
72
85
  barra de quien mira funciona, pero sigue siendo regla usar `avatarUrl`.
73
86
  - Ya trae el arreglo de iOS para el `:active` pegado.
74
87
 
88
+ ## Bien y mal
89
+
90
+ La foto y el nombre de la cuenta
91
+
92
+ ```html
93
+ <!-- ❌ mal: el correo empuja la barra y photoURL puede ser la foto de Google -->
94
+ <sui-topbar-user [name]="usuario().email" [avatar]="usuario().photoURL"
95
+ (clicked)="menuCuenta.set(!menuCuenta())" />
96
+ ```
97
+
98
+ ```html
99
+ <!-- ✅ bien -->
100
+ <sui-topbar-user [name]="usuario().nombre"
101
+ [avatar]="usuario().avatarUrl || { initials: usuario().iniciales, color: 'orange' }"
102
+ [open]="menuCuenta()" (clicked)="menuCuenta.set(!menuCuenta())" />
103
+ ```
104
+
105
+ Sin foto
106
+
107
+ ```html
108
+ <!-- ❌ mal: sin avatar sale la silueta gris, y verified no se usa en esta app -->
109
+ <sui-topbar-user name="Sophia" [verified]="true" />
110
+ ```
111
+
112
+ ```html
113
+ <!-- ✅ bien -->
114
+ <sui-topbar-user name="Sophia" [avatar]="{ initials: 'S', color: 'orange' }" />
115
+ ```
116
+
117
+ El repartidor de un pedido
118
+
119
+ ```html
120
+ <!-- ❌ mal: es otra persona; la flecha promete un menú que no existe -->
121
+ <sui-topbar-user [name]="pedido().repartidorNombre" [avatar]="pedido().repartidorFoto" />
122
+ ```
123
+
124
+ ```html
125
+ <!-- ✅ bien -->
126
+ <sui-avatar [size]="bp.sizeAvatar('md')" [src]="pedido().repartidorFoto"
127
+ [alt]="pedido().repartidorNombre" />
128
+ <span>{{ pedido().repartidorNombre }}</span>
129
+ ```
130
+
75
131
  ## Ejemplo
76
132
 
77
133
  ```html