@ajuarezso/sui-design-system 0.24.1 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/docs/sui-accordion/README.md +85 -11
  2. package/docs/sui-activity-feed/README.md +65 -11
  3. package/docs/sui-activity-filter/README.md +54 -5
  4. package/docs/sui-alert/README.md +54 -0
  5. package/docs/sui-app-store-badge/README.md +64 -10
  6. package/docs/sui-avatar/README.md +92 -29
  7. package/docs/sui-avatar-group/README.md +75 -9
  8. package/docs/sui-badge/README.md +85 -17
  9. package/docs/sui-banner/README.md +85 -20
  10. package/docs/sui-brand-logo/README.md +95 -31
  11. package/docs/sui-breadcrumb-item/README.md +54 -3
  12. package/docs/sui-breadcrumbs/README.md +54 -6
  13. package/docs/sui-button/README.md +76 -12
  14. package/docs/sui-button-group/README.md +91 -23
  15. package/docs/sui-button-group-item/README.md +81 -14
  16. package/docs/sui-card/README.md +105 -18
  17. package/docs/sui-chart-legend/README.md +63 -10
  18. package/docs/sui-chart-legend-dot/README.md +59 -10
  19. package/docs/sui-checkbox/README.md +109 -29
  20. package/docs/sui-checkbox-card/README.md +70 -14
  21. package/docs/sui-checkbox-label/README.md +92 -26
  22. package/docs/sui-circular-progress-bar/README.md +80 -19
  23. package/docs/sui-color-dot/README.md +72 -12
  24. package/docs/sui-color-picker/README.md +71 -13
  25. package/docs/sui-command-menu-footer/README.md +50 -1
  26. package/docs/sui-command-menu-item/README.md +74 -14
  27. package/docs/sui-command-menu-search/README.md +58 -3
  28. package/docs/sui-compact-avatar-group/README.md +73 -13
  29. package/docs/sui-compact-button/README.md +123 -34
  30. package/docs/sui-compact-select/README.md +61 -7
  31. package/docs/sui-compact-select-for-input/README.md +70 -6
  32. package/docs/sui-content-card/README.md +75 -14
  33. package/docs/sui-content-divider/README.md +67 -16
  34. package/docs/sui-content-label/README.md +99 -20
  35. package/docs/sui-counter-input/README.md +102 -18
  36. package/docs/sui-date-selector/README.md +69 -11
  37. package/docs/sui-datepicker/README.md +85 -18
  38. package/docs/sui-day-cell/README.md +75 -17
  39. package/docs/sui-day-label/README.md +56 -7
  40. package/docs/sui-divider/README.md +64 -9
  41. package/docs/sui-drawer-footer/README.md +63 -2
  42. package/docs/sui-drawer-header/README.md +69 -12
  43. package/docs/sui-dropdown-caption/README.md +58 -6
  44. package/docs/sui-dropdown-item/README.md +75 -9
  45. package/docs/sui-dropdown-menu/README.md +83 -14
  46. package/docs/sui-dropdown-search/README.md +60 -4
  47. package/docs/sui-emoji/README.md +94 -30
  48. package/docs/sui-empty-state/README.md +71 -7
  49. package/docs/sui-fancy-button/README.md +74 -14
  50. package/docs/sui-file-icon/README.md +70 -12
  51. package/docs/sui-file-upload/README.md +77 -17
  52. package/docs/sui-file-upload-card/README.md +64 -12
  53. package/docs/sui-filter/README.md +83 -14
  54. package/docs/sui-flag/README.md +73 -8
  55. package/docs/sui-form-hint/README.md +56 -7
  56. package/docs/sui-form-label/README.md +57 -5
  57. package/docs/sui-href/README.md +47 -0
  58. package/docs/sui-icon/README.md +86 -18
  59. package/docs/sui-image-upload/README.md +90 -22
  60. package/docs/sui-image-upload-profile/README.md +70 -11
  61. package/docs/sui-img-retry/README.md +50 -1
  62. package/docs/sui-inline-input/README.md +67 -9
  63. package/docs/sui-inline-select/README.md +62 -2
  64. package/docs/sui-input/README.md +90 -27
  65. package/docs/sui-integration-switch/README.md +84 -17
  66. package/docs/sui-kbd/README.md +73 -9
  67. package/docs/sui-key-icon/README.md +85 -23
  68. package/docs/sui-link-button/README.md +96 -27
  69. package/docs/sui-logo/README.md +93 -22
  70. package/docs/sui-modal/README.md +72 -13
  71. package/docs/sui-modal-footer/README.md +86 -12
  72. package/docs/sui-modal-header/README.md +57 -1
  73. package/docs/sui-modal-sheet/README.md +74 -12
  74. package/docs/sui-notification/README.md +81 -11
  75. package/docs/sui-notification-footer/README.md +55 -5
  76. package/docs/sui-notification-tab-menu/README.md +61 -5
  77. package/docs/sui-otp-input/README.md +70 -9
  78. package/docs/sui-page-header/README.md +89 -19
  79. package/docs/sui-pagination/README.md +73 -13
  80. package/docs/sui-password-strength/README.md +50 -1
  81. package/docs/sui-payment-icon/README.md +80 -18
  82. package/docs/sui-period-range/README.md +70 -11
  83. package/docs/sui-popover/README.md +81 -15
  84. package/docs/sui-product-card/README.md +89 -21
  85. package/docs/sui-progress-bar/README.md +82 -15
  86. package/docs/sui-progress-bar-label/README.md +79 -18
  87. package/docs/sui-radio/README.md +84 -7
  88. package/docs/sui-radio-card/README.md +70 -16
  89. package/docs/sui-radio-label/README.md +89 -22
  90. package/docs/sui-rating/README.md +101 -31
  91. package/docs/sui-rich-editor/README.md +47 -2
  92. package/docs/sui-segmented-control/README.md +90 -21
  93. package/docs/sui-select/README.md +80 -11
  94. package/docs/sui-sidebar/README.md +71 -16
  95. package/docs/sui-slider/README.md +75 -8
  96. package/docs/sui-social-button/README.md +71 -11
  97. package/docs/sui-status-badge/README.md +75 -14
  98. package/docs/sui-stepper/README.md +75 -18
  99. package/docs/sui-store-card/README.md +85 -22
  100. package/docs/sui-switch/README.md +82 -11
  101. package/docs/sui-switch-card/README.md +80 -16
  102. package/docs/sui-switch-label/README.md +91 -14
  103. package/docs/sui-tab-chips/README.md +93 -21
  104. package/docs/sui-tab-menu-horizontal/README.md +68 -7
  105. package/docs/sui-tab-menu-vertical/README.md +64 -11
  106. package/docs/sui-table/README.md +77 -16
  107. package/docs/sui-tag/README.md +86 -12
  108. package/docs/sui-tag-input/README.md +77 -13
  109. package/docs/sui-textarea/README.md +84 -17
  110. package/docs/sui-time-picker/README.md +96 -33
  111. package/docs/sui-toast/README.md +81 -20
  112. package/docs/sui-tooltip/README.md +80 -22
  113. package/docs/sui-topbar/README.md +64 -10
  114. package/docs/sui-topbar-button/README.md +61 -7
  115. package/docs/sui-topbar-item/README.md +68 -7
  116. package/docs/sui-topbar-user/README.md +61 -5
  117. package/fesm2022/ajuarezso-sui-design-system.mjs +15 -10
  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 +7 -3
@@ -26,8 +26,8 @@ propios del DS (`info-custom-fill`, `heart-half-fill`…) también pasan por aqu
26
26
 
27
27
  > **La guía del DS no tiene página para el ícono**: usa RemixIcon en todos sus ejemplos, sin
28
28
  > reglas de tamaño. La escala de abajo sale de los tokens del DS (`--sui-icon-size-*` y
29
- > `--sui-icon-*` en `sui-tokens.scss`). Lo marcado «canon» ya es regla del proyecto; el resto es
30
- > **propuesta, falta aprobación**.
29
+ > `--sui-icon-*` en `sui-tokens.scss`). Lo marcado «canon» ya es regla del proyecto; lo que sigue
30
+ > en **propuesta** espera decisión.
31
31
  >
32
32
  > Uso medido el 03-10-2026 en `master-pedidos/src/app`: 406 íconos en 95 archivos, sin contar
33
33
  > prototipos ni vitrinas. 331 `-line`, 43 `-fill`. Ninguno usa el input `color`.
@@ -36,14 +36,14 @@ propios del DS (`info-custom-fill`, `heart-half-fill`…) también pasan por aqu
36
36
 
37
37
  | valor | tamaño de hoy | cuándo | fuente |
38
38
  |---|---|---|---|
39
- | `12` | 5 | Dentro de un badge `small`. | propuesta (token `--sui-icon-size-xs`) |
40
- | `16` | 69 | Junto a texto chico: metadatos, botones `2xsmall`/`xsmall`, dentro de un kbd. | propuesta (token `--sui-icon-size-sm`) |
41
- | `20` (default) | 114 | El normal: botones, inputs, filas, navegación. | propuesta (token `--sui-icon-size-md`) |
42
- | `24` | 5 | Cabeceras y acciones sueltas. | propuesta (token `--sui-icon-size-lg`) |
43
- | `32` | 2 | Ícono protagonista. Si va dentro de un círculo, es `sui-key-icon`. | propuesta (token `--sui-icon-size-xl`) |
39
+ | `12` | 5 | Dentro de un badge `small`. | canon (aprobado por el dueño, 05-10-2026, bloque B1), token `--sui-icon-size-xs` |
40
+ | `16` | 69 | Junto a texto chico: metadatos, botones `2xsmall`/`xsmall`, dentro de un kbd. | canon (aprobado por el dueño, 05-10-2026, bloque B1), token `--sui-icon-size-sm` |
41
+ | `20` (default) | 114 | El normal: botones, inputs, filas, navegación. | canon (aprobado por el dueño, 05-10-2026, bloque B1), token `--sui-icon-size-md` |
42
+ | `24` | 5 | Cabeceras y acciones sueltas. | canon (aprobado por el dueño, 05-10-2026, bloque B1), token `--sui-icon-size-lg` |
43
+ | `32` | 2 | Ícono protagonista. Si va dentro de un círculo, es `sui-key-icon`. | canon (aprobado por el dueño, 05-10-2026, bloque B1), token `--sui-icon-size-xl` |
44
44
  | `26` | 7 | Solo el ícono del hero de los onboarding (`ob__hero-icon`). | canon (`CLAUDE.md`, patrón de los pasos de onboarding) |
45
45
  | `14`, `18` | 149 | Fuera de la escala, pero el DS mismo los usa (tabs, product-card). **A discutir**: no migrar en masa. | propuesta |
46
- | `13`, `22`, `28`, `44`… | 49 | Sin regla: van al más cercano de la escala. | propuesta |
46
+ | `13`, `22`, `28`, `44`… | 49 | Sin regla: van al más cercano de la escala. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
47
47
 
48
48
  **Usos de hoy fuera de la escala sin regla: 49.** `22` (20 usos: `components/ota-quick-fab/ota-quick-fab.html:11`,
49
49
  `pages/schedule/schedule.html:10`), `13` (14: `pages/super-admin/master-drivers/master-drivers.html:359`, `:419`),
@@ -56,18 +56,18 @@ propios del DS (`info-custom-fill`, `heart-half-fill`…) también pasan por aqu
56
56
 
57
57
  | valor | cuándo | fuente |
58
58
  |---|---|---|
59
- | `-line` | El normal. | propuesta |
60
- | `-fill` | El seleccionado (el tab activo) o un glifo chico sobre color. | propuesta |
61
- | propios del DS (`info-custom-fill`, `heart-half-fill`, `building-skyline`, `avatar-placeholder`) | Los usan componentes del DS; en la app no hacen falta. | propuesta |
59
+ | `-line` | El normal. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
60
+ | `-fill` | El seleccionado (el tab activo) o un glifo chico sobre color. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
61
+ | propios del DS (`info-custom-fill`, `heart-half-fill`, `building-skyline`, `avatar-placeholder`) | Los usan componentes del DS; en la app no hacen falta. | canon (aprobado por el dueño, 05-10-2026, bloque B6) |
62
62
 
63
63
  ### `color`
64
64
 
65
65
  | valor | cuándo | fuente |
66
66
  |---|---|---|
67
- | sin `color` (default) | El normal: hereda el color del texto. Casi siempre es lo que se quiere. | propuesta |
68
- | `var(--sui-icon-sub-600)` | Ícono de apoyo junto a un texto principal. | propuesta |
69
- | `var(--sui-icon-soft-400)` | Decorativo, metadatos. | propuesta |
70
- | `var(--sui-icon-disabled-300)` | Deshabilitado. | propuesta |
67
+ | sin `color` (default) | El normal: hereda el color del texto. Casi siempre es lo que se quiere. | canon (aprobado por el dueño, 05-10-2026, bloque B3) |
68
+ | `var(--sui-icon-sub-600)` | Ícono de apoyo junto a un texto principal. | canon (CANON-SUI §4.1: los íconos siguen la semántica del texto; sub-600 = secundario que igual se lee) |
69
+ | `var(--sui-icon-soft-400)` | Decorativo, metadatos. | canon (CANON-SUI §4.1: soft-400 = lo que no es contenido, metadata terciaria) |
70
+ | `var(--sui-icon-disabled-300)` | Deshabilitado. | canon (CANON-SUI §4.1: disabled-300 solo para disabled) |
71
71
  | `var(--sui-icon-white-0)` | Sobre fondo oscuro o de color. | propuesta |
72
72
  | un hex o un color de estado | No usar: si el ícono dice un estado, va en `sui-key-icon` `lighter`. | propuesta |
73
73
 
@@ -76,7 +76,10 @@ propios del DS (`info-custom-fill`, `heart-half-fill`…) también pasan por aqu
76
76
  En mobile se lee y se toca con el dedo, así que va más grande; en web, más chico. En las
77
77
  pantallas que se usan en los dos (panel del local, super admin) se elige con el servicio de
78
78
  breakpoints de la app (`bp`, en `services/breakpoints.service.ts`); en las que solo existen en
79
- el celular (cliente, repartidor) va directo el valor de mobile. Todo es **propuesta**.
79
+ el celular (cliente, repartidor) también se usa `bp`, porque el celular chico va con el valor de
80
+ web. No se toca, así que la tablet va con el valor de web, y el celular chico (< 380 px) también (D16).
81
+ Es **canon (dueño, 05-10-2026, P1 b)**: un paso más grande en el celular que en computadora, y
82
+ se migra todo ahora, no solo al tocar cada pantalla.
80
83
 
81
84
  | uso | mobile | web |
82
85
  |---|---|---|
@@ -86,12 +89,36 @@ el celular (cliente, repartidor) va directo el valor de mobile. Todo es **propue
86
89
  | dentro de un botón o un input | lo decide el componente | igual |
87
90
 
88
91
  ```html
89
- <sui-icon name="store-2-line" [size]="bp.isMobile() ? 20 : 16" />
92
+ <sui-icon name="store-2-line" [size]="bp.celular() ? 20 : 16" />
90
93
  ```
91
94
 
92
95
  Un ícono suelto **nunca** es el área táctil: si se toca, va en `sui-compact-button` o
93
96
  `sui-button` `onlyIcon`, que dan los 44 px. Hoy solo 2 íconos cambian por breakpoint
94
- (`pages/discounts/discounts.html:103` y `:108`, con `bp.isTablet() ? 20 : 16`).
97
+ (`pages/discounts/discounts.html:103` y `:108`, con `bp.isTablet() ? 20 : 16`), y los dos están
98
+ mal: van dentro de un `sui-button`, que ya le da al ícono una caja de 20, así que no llevan
99
+ `size`. Hasta 1024 px dibujan 20 y llenan la caja (no se nota); en una computadora de más de
100
+ 1024 px dibujan 16 y el glifo queda chico dentro de esa caja. Además `isTablet` no es un corte
101
+ de D16 (ver «Bien y mal»).
102
+
103
+ ### Por dispositivo (D16)
104
+
105
+ El ícono no se toca: sube un paso **solo en el celular de 380 px o más**. Fuente: D16, dueño
106
+ 05-10-2026 (P1 b).
107
+
108
+ | dispositivo | size | nota |
109
+ |---|---|---|
110
+ | celular chico (< 380) | valor «web» (`16` en una fila, `24` protagonista) | no se toca: no sube aunque sea un celular |
111
+ | celular (380–767) | valor «mobile» (`20` junto al texto de una fila, `32` protagonista) | un paso más que en la computadora |
112
+ | tablet táctil (`pointer: coarse`) | valor «web» (`16` en una fila, `24` protagonista) | no se toca: el puntero no cambia nada |
113
+ | computadora (`pointer: fine`) | valor «web» (`16` en una fila, `24` protagonista) | |
114
+
115
+ En el ultrawide, igual que en la computadora: el espacio extra va a más columnas, no a íconos
116
+ más grandes. Dentro de un botón o un input no se pasa `size`: lo decide el componente.
117
+
118
+ En master-pedidos no hay helper: `[size]="bp.celular() ? 20 : 16"` (fila) o
119
+ `[size]="bp.celular() ? 32 : 24"` (protagonista). No uses `bp.isMobile()` (incluye el celular
120
+ chico) ni `bp.isTablet()` (incluye la tablet y la laptop de hasta 1024 px): los dos van con el
121
+ valor de web.
95
122
 
96
123
  ## «No usar»: por qué no hoy y cuándo sí
97
124
 
@@ -128,6 +155,47 @@ Outputs: ninguno.
128
155
  el paquete del DS no la trae.
129
156
  - Dentro de un `sui-kbd`, el kbd le fuerza el color `text-sub-600`.
130
157
 
158
+ ## Bien y mal
159
+
160
+ Ícono de una acción en una tabla
161
+
162
+ ```html
163
+ <!-- ❌ mal: el botón ya le da al ícono su caja de 20 y no hace falta size; en la computadora de más de 1024 px isTablet dibuja el glifo en 16 dentro de ella (pages/discounts/discounts.html:103) -->
164
+ <sui-button variant="ghost" color="neutral" [size]="bp.sizeControl()" [onlyIcon]="true" aria-label="Editar">
165
+ <sui-icon data-slot="start" name="edit-2-line" [size]="bp.isTablet() ? 20 : 16" />
166
+ </sui-button>
167
+ ```
168
+
169
+ ```html
170
+ <!-- ✅ bien -->
171
+ <sui-button variant="ghost" color="neutral" [size]="bp.sizeControl()" [onlyIcon]="true"
172
+ leftIcon="edit-2-line" aria-label="Editar" />
173
+ ```
174
+
175
+ Ícono protagonista
176
+
177
+ ```html
178
+ <!-- ❌ mal: 28 está fuera de la escala y no cambia por dispositivo (pages/envios/envios.html:712) -->
179
+ <sui-icon class="seg__buscando-icono" name="e-bike-2-line" [size]="28" />
180
+ ```
181
+
182
+ ```html
183
+ <!-- ✅ bien -->
184
+ <sui-icon class="seg__buscando-icono" name="e-bike-2-line" [size]="bp.celular() ? 32 : 24" />
185
+ ```
186
+
187
+ Tamaño como texto
188
+
189
+ ```html
190
+ <!-- ❌ mal: un string sin unidad no es un tamaño CSS válido; el ícono no toma tamaño (Trampas) -->
191
+ <sui-icon name="time-line" size="20" />
192
+ ```
193
+
194
+ ```html
195
+ <!-- ✅ bien -->
196
+ <sui-icon name="time-line" [size]="20" />
197
+ ```
198
+
131
199
  ## Ejemplo
132
200
 
133
201
  ```html
@@ -27,8 +27,9 @@ El archivo que entrega **no se sube tal cual**: pasa por `ImageCompressorService
27
27
  ## Qué variante usar
28
28
 
29
29
  > **La guía del DS no tiene página para este componente** (el «Image Upload» del Figma de origen
30
- > es otra cosa: la foto con botones, que en SUI es `sui-image-upload-profile`). Todo es
31
- > **propuesta, falta aprobación**.
30
+ > es otra cosa: la foto con botones, que en SUI es `sui-image-upload-profile`). Las reglas
31
+ > marcadas canon están aprobadas y las de «según el código» describen el componente; lo que sigue
32
+ > en **propuesta** espera decisión.
32
33
  >
33
34
  > Uso medido el 03-10-2026 en `master-pedidos/src/app` (sin prototipos ni vitrinas): **13 usos en
34
35
  > 10 pantallas**. `shape`: 11 `rect`, 2 `circle`. `size`: 7 `xl`, 3 `lg`, 3 sin size (fluidos).
@@ -38,16 +39,16 @@ El archivo que entrega **no se sube tal cual**: pasa por `ImageCompressorService
38
39
 
39
40
  | valor | cuándo | fuente |
40
41
  |---|---|---|
41
- | `circle` (default) | **Solo la foto de una persona** (perfil del cliente, del repartidor, del usuario del local). Así se ve después en `sui-avatar`. | propuesta |
42
- | `rect` | **Todo lo demás**: el logo del local (en el home se ve como cuadrado redondeado), la portada, la foto del producto, comprobantes, QR, banners. | propuesta |
42
+ | `circle` (default) | **Solo la foto de una persona** (perfil del cliente, del repartidor, del usuario del local). Así se ve después en `sui-avatar`. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
43
+ | `rect` | **Todo lo demás**: el logo del local (en el home se ve como cuadrado redondeado), la portada, la foto del producto, comprobantes, QR, banners. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
43
44
 
44
45
  ### `size` y `fluid` — según la forma de la imagen
45
46
 
46
47
  | valor | px | cuándo | fuente |
47
48
  |---|---|---|---|
48
- | `fluid` | ancho del contenedor, alto hasta 320 | **Capturas y documentos** (comprobante, Yape, QR) y **imágenes apaisadas** (portada 16:9, banner). Una captura de celular es vertical: en un cuadrado se recorta. | propuesta |
49
- | `xl` | 200 | **Imágenes cuadradas**: logo, foto del producto, foto de perfil. | propuesta |
50
- | `lg` (default) | 160 | Imagen cuadrada al lado de otra cosa en la misma fila (el logo junto al nombre del local en web). | propuesta |
49
+ | `fluid` | ancho del contenedor, alto hasta 320 | **Capturas y documentos** (comprobante, Yape, QR) y **imágenes apaisadas** (portada 16:9, banner). Una captura de celular es vertical: en un cuadrado se recorta. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
50
+ | `xl` | 200 | **Imágenes cuadradas**: logo, foto del producto, foto de perfil. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
51
+ | `lg` (default) | 160 | Imagen cuadrada al lado de otra cosa en la misma fila (el logo junto al nombre del local en web). | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
51
52
  | `md` | 120 | No usar: chico para ver si la foto quedó bien. | no usar |
52
53
  | `sm` | 80 | No usar: no entra el texto (el subtítulo se esconde). | no usar |
53
54
 
@@ -55,25 +56,25 @@ El archivo que entrega **no se sube tal cual**: pasa por `ImageCompressorService
55
56
 
56
57
  | valor | cuándo | fuente |
57
58
  |---|---|---|
58
- | `true` | **Siempre.** Con imagen muestra dos botones a la vista: lápiz y tacho. El lápiz emite `editRequested`: si la pantalla tiene recortador, lo abre (`app-image-cropper`); si no, llama a `abrirSelector()` para elegir otra (como el Yape del repartidor). | propuesta |
59
+ | `true` | **Siempre.** Con imagen muestra dos botones a la vista: lápiz y tacho. El lápiz emite `editRequested`: si la pantalla tiene recortador, lo abre (`app-image-cropper`); si no, llama a `abrirSelector()` para elegir otra (como el Yape del repartidor). | canon (aprobado por el dueño, 05-10-2026, bloque B8) |
59
60
  | `false` (default) | No usar: con imagen, el lápiz aparece **solo al pasar el mouse** (en el celular no se ve) y el «Quitar» de abajo es un botón aparte (32 px). | no usar |
60
61
 
61
62
  ### Textos: `title`, `subtitle`
62
63
 
63
64
  | input | cuándo | fuente |
64
65
  |---|---|---|
65
- | `title` | Verbo + qué: «Subir logo», «Subir comprobante», «Subir captura del pago». | propuesta |
66
- | `subtitle` | Formatos y tamaño, **solo los que el componente acepta** (JPG, PNG, WebP, GIF o HEIC), o el encuadre que se va a aplicar («se recorta a 16:9»). Mientras procesa: «Procesando…». | propuesta |
66
+ | `title` | Verbo en imperativo con tuteo + qué: «Sube tu logo», «Sube el comprobante», «Sube la captura del pago». Sin `title`: «Sube tu foto» (`circle`) o «Sube tu logo» (`rect`). | canon (dueño, 05-10-2026, P4 a); el default cambió en la 0.25.0 (antes «Subir foto» / «Subir logo») |
67
+ | `subtitle` | Formatos y tamaño, **solo los que el componente acepta** (JPG, PNG, WebP, GIF o HEIC), o el encuadre que se va a aplicar («se recorta a 16:9»). Mientras procesa: «Procesando…». | canon (aprobado por el dueño, 05-10-2026, bloque B4) |
67
68
 
68
69
  ### `maxSize`, `disabled`, `src`
69
70
 
70
71
  | input | cuándo | fuente |
71
72
  |---|---|---|
72
- | `maxSize` | Siempre, en **bytes**. 10 MB para capturas y comprobantes, 20–50 MB para fotos que se van a comprimir. | propuesta |
73
- | `disabled` | Mientras se comprime o se sube (`subiendo()`), para que no se elija otra encima. | propuesta |
74
- | `src` (model) | La URL guardada, o la vista previa local mientras se sube. Con `[(src)]` el componente la pone solo al elegir. | propuesta |
73
+ | `maxSize` | Siempre, en **bytes**. 10 MB para capturas y comprobantes, 20–50 MB para fotos que se van a comprimir. | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
74
+ | `disabled` | Mientras se comprime o se sube (`subiendo()`), para que no se elija otra encima. | canon (aprobado por el dueño, 05-10-2026, bloque B8) |
75
+ | `src` (model) | La URL guardada, o la vista previa local mientras se sube. Con `[(src)]` el componente la pone solo al elegir. | según el código |
75
76
 
76
- ### Usos de hoy que rompen la propuesta
77
+ ### Usos de hoy que rompen la regla
77
78
 
78
79
  - **5 subtítulos prometen HEIC**: hasta el 03-10-2026 el componente lo rechazaba; desde la 0.22.0
79
80
  (la que usa la app) ya lo acepta, así que **ya no rompen nada**. Lo que queda es la vista previa en
@@ -99,8 +100,8 @@ El archivo que entrega **no se sube tal cual**: pasa por `ImageCompressorService
99
100
  En mobile se toca con el dedo: lo que se toca mide **44 px o más** (canon (dueño, 04-10-2026): piso táctil 44 en el teléfono). El área vacía se
100
101
  toca entera (80 a 200 px), así que pasa. Los botones con imagen se **ven** de 40 (lápiz y tacho) y
101
102
  32 («Quitar»), pero en el teléfono (≤ 767 px) **se tocan en 44 de alto** con un `::before`
102
- invisible (04-10-2026). En pantallas de los dos se elige con el servicio de breakpoints de la app
103
- (`bp`, en `services/breakpoints.service.ts`).
103
+ invisible (04-10-2026). Aquí `bp` no elige nada: `size` va por la forma de la imagen y lo que se
104
+ toca ya tiene su área sola (ver «Por dispositivo (D16)»).
104
105
 
105
106
  **Por qué no hay un size nuevo**: aquí `size` (`sm`/`md`/`lg`/`xl`) es el tamaño de la **foto**, no
106
107
  el de los botones. Un `2xl` agrandaría la imagen sin que nadie lo pida; el área invisible lleva lo
@@ -108,14 +109,14 @@ que se toca a 44 sin cambiar el dibujo ni los sizes de hoy.
108
109
 
109
110
  | uso | mobile | web | fuente |
110
111
  |---|---|---|---|
111
- | comprobante, captura, QR, portada, banner | `fluid` | `fluid` (en una columna de ≤ 480 px) | propuesta |
112
- | logo, foto de producto, foto de perfil | `xl` (200) | `xl` (200), o `lg` (160) al lado del nombre | propuesta |
112
+ | comprobante, captura, QR, portada, banner | `fluid` | `fluid` (en una columna de ≤ 480 px) | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
113
+ | logo, foto de producto, foto de perfil | `xl` (200) | `xl` (200), o `lg` (160) al lado del nombre | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
113
114
  | lápiz y tacho (`editable`) | se ven de 40 (32 en `sm`), se tocan en 44 de alto; a los lados el área crece la mitad del espacio entre los dos (3; 2 en `sm`) para no pisarse | 40 px | canon (dueño, 04-10-2026): piso táctil 44 en el teléfono (área) · canon (aprobado por el dueño, 03-10-2026), extensión SUI (40) |
114
115
  | «Quitar» (sin `editable`) | se ve de 32 (`sui-button` `xsmall`), se toca en 44 de alto: crece 4 hacia la foto (la mitad del gap de 8) y 8 hacia abajo | 32 px | canon (dueño, 04-10-2026): piso táctil 44 en el teléfono (área) · canon (aprobado por el dueño, 03-10-2026) |
115
116
 
116
117
  ```html
117
- <sui-image-upload shape="rect" [size]="bp.isMobile() ? 'xl' : 'lg'" [editable]="true"
118
- [src]="logo()" [maxSize]="50 * 1024 * 1024" title="Subir logo"
118
+ <sui-image-upload shape="rect" size="xl" [editable]="true"
119
+ [src]="logo()" [maxSize]="50 * 1024 * 1024" title="Sube tu logo"
119
120
  subtitle="JPG, PNG o WebP — máx 50 MB"
120
121
  (imageSelected)="alElegirLogo($event)" (editRequested)="abrirRecortador()"
121
122
  (removed)="quitarLogo()" />
@@ -126,6 +127,27 @@ de 40 y quedan en 32) y el «Quitar» es un `sui-button` `xsmall` de **32** (ant
126
127
  los dos botones van centrados abajo, dentro del círculo. Lo que haya justo debajo del «Quitar»
127
128
  pierde 8 px de toque en el teléfono: no pegarle otro control sin margen.
128
129
 
130
+ ### Por dispositivo (D16)
131
+
132
+ D16 (dueño, 05-10-2026) no mueve `size`: aquí `size` es el tamaño de la **foto**, no el de lo que
133
+ se toca, así que no sigue la escala «lo que se toca / lo que no se toca». Se elige por la forma de
134
+ la imagen (`xl` para lo cuadrado, `fluid` para capturas y apaisadas) y por el layout, igual que en
135
+ la tabla de arriba: `xl` en una columna, que es lo que hay en el celular, el celular chico y la
136
+ tablet; `lg` solo cuando la foto comparte la fila con el nombre (hoy ninguna pantalla lo hace:
137
+ los 3 `lg` medidos son el logo del onboarding y el banner y el comprobante de promociones, que
138
+ además van `fluid`; ninguno está en una fila).
139
+ Lo que decide entre `xl` y `lg` es si hay fila, no el dispositivo: cuando una pantalla arme esa
140
+ fila según el ancho, la condición es el mismo corte del layout de la pantalla, no
141
+ `bp.celular()` ni `bp.tactil()`.
142
+
143
+ Lo que se toca (lápiz, tacho, «Quitar») ya llega a 44 de alto en el celular y en el celular chico
144
+ con el área invisible (`@media (max-width: 767px)`). En la **tablet táctil** (`pointer: coarse`,
145
+ más de 767 px) esa área no se prende y quedan en 40 y 32: **pendiente**, espera el mixin
146
+ `sui-tactil` del DS que propone §0 de las decisiones (sin el sí del dueño todavía).
147
+
148
+ En master-pedidos: no hay helper de D16; `size="xl"` para lo cuadrado y `[fluid]="true"` para
149
+ capturas y apaisadas. `lg` solo en la fila del nombre, con la condición del layout de esa pantalla.
150
+
129
151
  ## «No usar»: por qué no hoy y cuándo sí
130
152
 
131
153
  Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
@@ -194,19 +216,65 @@ recortador; se llama con una referencia: `#qr` → `qr.abrirSelector()`).
194
216
  - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador).
195
217
  - Ya trae el arreglo de iOS para el `:active` pegado.
196
218
 
219
+ ## Bien y mal
220
+
221
+ Logo del local
222
+
223
+ ```html
224
+ <!-- ❌ mal: el logo se ve cuadrado en el home, no en círculo; en 160 (lg) en una columna, donde va xl; y sin lápiz (pages/business-onboarding/step-info/step-info.html:42) -->
225
+ <sui-image-upload shape="circle" size="lg" [(src)]="logoPreview" title="Subir logo"
226
+ (imageSelected)="onLogoSelected($event)" />
227
+ ```
228
+
229
+ ```html
230
+ <!-- ✅ bien -->
231
+ <sui-image-upload shape="rect" size="xl" [editable]="true" [(src)]="logoPreview" title="Sube tu logo"
232
+ (imageSelected)="onLogoSelected($event)" (editRequested)="abrirRecortador()" />
233
+ ```
234
+
235
+ Comprobante de pago
236
+
237
+ ```html
238
+ <!-- ❌ mal: una captura vertical se recorta en el cuadrado de 200 y el «Quitar» queda en 32 (pages/central/central-gastos/central-gastos.html:118) -->
239
+ <sui-image-upload shape="rect" size="xl" [src]="vistaComprobante()" [maxSize]="10 * 1024 * 1024"
240
+ title="Subir comprobante" (imageSelected)="onComprobante($event)" (removed)="quitarComprobante()" />
241
+ ```
242
+
243
+ ```html
244
+ <!-- ✅ bien -->
245
+ <sui-image-upload #comprobante shape="rect" [fluid]="true" [editable]="true" [src]="vistaComprobante()"
246
+ [maxSize]="10 * 1024 * 1024" title="Sube el comprobante" (imageSelected)="onComprobante($event)"
247
+ (editRequested)="comprobante.abrirSelector()" (removed)="quitarComprobante()" />
248
+ ```
249
+
250
+ Banner apaisado
251
+
252
+ ```html
253
+ <!-- ❌ mal: con fluid, size no hace nada; y sin editable (pages/promociones/promociones.html:186) -->
254
+ <sui-image-upload shape="rect" size="lg" [fluid]="true" [src]="campaignBannerPreview()"
255
+ title="Subir banner" (imageSelected)="onBannerImage($event)" />
256
+ ```
257
+
258
+ ```html
259
+ <!-- ✅ bien -->
260
+ <sui-image-upload #banner shape="rect" [fluid]="true" [editable]="true" [src]="campaignBannerPreview()"
261
+ title="Sube el banner" (imageSelected)="onBannerImage($event)"
262
+ (editRequested)="banner.abrirSelector()" (removed)="onBannerRemoved()" />
263
+ ```
264
+
197
265
  ## Ejemplo
198
266
 
199
267
  ```html
200
268
  <!-- Comprobante: ancho completo, el lápiz elige otra captura -->
201
269
  <sui-image-upload #comprobante shape="rect" [fluid]="true" [editable]="true"
202
270
  [src]="vistaPrevia()" [maxSize]="10 * 1024 * 1024"
203
- title="Subir comprobante" subtitle="Captura del Yape o del banco — máx 10 MB"
271
+ title="Sube el comprobante" subtitle="Captura del Yape o del banco — máx 10 MB"
204
272
  (imageSelected)="alElegir($event)" (editRequested)="comprobante.abrirSelector()"
205
273
  (removed)="quitar()" />
206
274
 
207
275
  <!-- Foto del producto: cuadrada, el lápiz abre el recortador -->
208
276
  <sui-image-upload shape="rect" size="xl" [editable]="true" [src]="foto()"
209
- title="Subir foto" subtitle="JPG, PNG o WebP — se comprime sola"
277
+ title="Sube tu foto" subtitle="JPG, PNG o WebP — se comprime sola"
210
278
  (imageSelected)="alElegir($event)" (editRequested)="abrirRecortador()"
211
279
  (removed)="quitarFoto()" />
212
280
  ```
@@ -27,7 +27,8 @@ chico (56–64 px) con su texto y los botones al lado, en una fila de un formula
27
27
  ## Qué variante usar
28
28
 
29
29
  > **La guía del DS no tiene página para este componente** (existe en el Figma de origen, sin
30
- > texto de uso). Todo es **propuesta, falta aprobación**.
30
+ > texto de uso). Las reglas marcadas canon están aprobadas y las de «según el código» describen
31
+ > el componente; lo que sigue en **propuesta** espera decisión.
31
32
  >
32
33
  > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. La foto de perfil va con
33
34
  > `app-avatar-editor` (3 pantallas).
@@ -36,27 +37,27 @@ chico (56–64 px) con su texto y los botones al lado, en una fila de un formula
36
37
 
37
38
  | valor | cuándo | fuente |
38
39
  |---|---|---|
39
- | `avatar` (default) | La foto de una persona. Vacío muestra una silueta. | propuesta |
40
- | `company` | Un negocio. Vacío muestra edificios. Ojo: lo muestra en círculo. | propuesta |
40
+ | `avatar` (default) | La foto de una persona. Vacío muestra una silueta. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
41
+ | `company` | Un negocio. Vacío muestra edificios. Ojo: lo muestra en círculo. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
41
42
 
42
43
  ### `alignment`
43
44
 
44
45
  | valor | cuándo | fuente |
45
46
  |---|---|---|
46
- | `vertical` (default) | Con texto: título y requisitos («Mín. 400 × 400 px, PNG o JPEG»), avatar de 64. | propuesta |
47
- | `horizontal` | Sin texto, solo los botones al lado del avatar de 56. Cuando el label ya está arriba (`sui-form-label`). | propuesta |
47
+ | `vertical` (default) | Con texto: título y requisitos («Mín. 400 × 400 px, PNG o JPEG»), avatar de 64. | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
48
+ | `horizontal` | Sin texto, solo los botones al lado del avatar de 56. Cuando el label ya está arriba (`sui-form-label`). | canon (aprobado por el dueño, 05-10-2026, bloque B2) |
48
49
 
49
50
  ### Textos y estados
50
51
 
51
52
  | input | cuándo | fuente |
52
53
  |---|---|---|
53
- | `title` | Qué es: «Tu foto», «Foto de perfil». Solo se ve en `vertical`. | propuesta |
54
- | `subtitle` | Requisitos reales: formatos que se aceptan y para qué se usa («La ve el cliente cuando llevas su pedido»). | propuesta |
54
+ | `title` | Qué es: «Tu foto», «Foto de perfil». Solo se ve en `vertical`. | canon (aprobado por el dueño, 05-10-2026, bloque B4) |
55
+ | `subtitle` | Requisitos reales: formatos que se aceptan y para qué se usa («La ve el cliente cuando llevas su pedido»). | canon (aprobado por el dueño, 05-10-2026, bloque B4) |
55
56
  | `labels` | Ya vienen en español con tuteo (Subir, Eliminar, Cambiar). Cambiarlos solo si el contexto pide otro verbo («Quitar»). | propuesta |
56
- | `imageUrl` | La URL guardada. Con imagen muestra Eliminar + Cambiar. | propuesta |
57
- | `companyLogoSrc` | Solo con `type="company"`: el logo, que gana sobre `imageUrl`. | propuesta |
58
- | `loading` | **Siempre** mientras se comprime y se sube: deshabilita los botones y atenúa la foto. | propuesta |
59
- | `disabled` | La persona no puede cambiarla (cuenta verificada con foto bloqueada). | propuesta |
57
+ | `imageUrl` | La URL guardada. Con imagen muestra Eliminar + Cambiar. | según el código |
58
+ | `companyLogoSrc` | Solo con `type="company"`: el logo, que gana sobre `imageUrl`. | según el código |
59
+ | `loading` | **Siempre** mientras se comprime y se sube: deshabilita los botones y atenúa la foto. | canon (aprobado por el dueño, 05-10-2026, bloque B8) |
60
+ | `disabled` | La persona no puede cambiarla (cuenta verificada con foto bloqueada). | canon (aprobado por el dueño, 05-10-2026, bloque B8) |
60
61
 
61
62
  Usos de hoy que rompen la regla: **0**.
62
63
 
@@ -80,6 +81,22 @@ Usos de hoy que rompen la regla: **0**.
80
81
  **Sin tamaño suficiente para mobile.** Hace falta en el DS un `size` que lleve los botones a
81
82
  `large` (48 px). Mientras tanto, en el celular va `sui-image-upload` `circle` `xl`.
82
83
 
84
+ ### Por dispositivo (D16)
85
+
86
+ No aplica: **no tiene `size`** y nada cambia por dispositivo (avatar de 64/56 y botones `xsmall`
87
+ de 32 fijos). Con D16 (dueño, 05-10-2026) lo que se toca va `large` en el celular y en la tablet
88
+ táctil, y esos botones no llegan a 44, así que en una pantalla táctil no se usa:
89
+
90
+ | dispositivo | qué va | ¿llega a 44? |
91
+ |---|---|---|
92
+ | celular y celular chico (≤ 767) | `sui-image-upload` `circle` `xl` con `[editable]="true"` | sí: su lápiz y su tacho se tocan en 44 con el área invisible |
93
+ | tablet táctil (`pointer: coarse`, > 767) | `sui-image-upload` `circle` `xl` con `[editable]="true"` (40 contra 32, el que más se acerca) | **no, pendiente**: el área de 44 de `sui-image-upload` se prende solo hasta 767 px y en la tablet quedan en 40; espera el mixin `sui-tactil` del DS (§0 de las decisiones, sin el sí del dueño todavía) |
94
+ | computadora y ultrawide | este componente, botones de 32 | no hace falta 44; se queda en 32 porque solo tiene ese tamaño de botón. Subirlo a `medium` es código del DS (un `size`), **pendiente** |
95
+
96
+ En master-pedidos: no lleva `[size]`; si la pantalla se usa en un dispositivo táctil,
97
+ `@if (!bp.tactil())` para este componente y `sui-image-upload` en el otro caso (con la tablet
98
+ todavía en 40, ver la tabla).
99
+
83
100
  ## «No usar»: por qué no hoy y cuándo sí
84
101
 
85
102
  Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
@@ -136,6 +153,48 @@ interface SuiImageUploadProfileLabels { upload?: string; remove?: string; change
136
153
  - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador); el `title`
137
154
  sí se usa como `alt` de la foto.
138
155
 
156
+ ## Bien y mal
157
+
158
+ Logo del local
159
+
160
+ ```html
161
+ <!-- ❌ mal: company lo muestra en círculo y el logo se ve cuadrado en el home (vitrina: pages/super-admin/master-preview/_guia-moleculas/fechas.ts:233) -->
162
+ <sui-image-upload-profile type="company" title="Logo del local" subtitle="JPG o PNG, cuadrado" />
163
+ ```
164
+
165
+ ```html
166
+ <!-- ✅ bien -->
167
+ <sui-image-upload shape="rect" size="xl" [editable]="true" [src]="logo()" title="Sube tu logo"
168
+ (imageSelected)="alElegirLogo($event)" (editRequested)="abrirRecortador()" />
169
+ ```
170
+
171
+ Subir la foto
172
+
173
+ ```html
174
+ <!-- ❌ mal: no tiene input de archivo; «Subir» y «Cambiar» solo emiten y no abren nada (Trampas) -->
175
+ <sui-image-upload-profile title="Foto de perfil" [imageUrl]="foto()" (upload)="subir()" />
176
+ ```
177
+
178
+ ```html
179
+ <!-- ✅ bien -->
180
+ <sui-image-upload-profile title="Foto de perfil" [imageUrl]="foto()" [loading]="subiendo()"
181
+ (upload)="input.click()" (change)="input.click()" (remove)="quitar()" />
182
+ <input #input type="file" accept="image/jpeg,image/png,image/webp" hidden (change)="alElegir($event)" />
183
+ ```
184
+
185
+ Foto de perfil en el celular
186
+
187
+ ```html
188
+ <!-- ❌ mal: los botones miden 32 y en una pantalla táctil no llegan a 44 (D16) -->
189
+ <sui-image-upload-profile title="Tu foto" [imageUrl]="foto()" (upload)="input.click()" />
190
+ ```
191
+
192
+ ```html
193
+ <!-- ✅ bien -->
194
+ <sui-image-upload shape="circle" size="xl" [editable]="true" [src]="foto()" title="Sube tu foto"
195
+ (imageSelected)="alElegir($event)" (editRequested)="abrirRecortador()" />
196
+ ```
197
+
139
198
  ## Ejemplo
140
199
 
141
200
  ```html
@@ -45,13 +45,19 @@ No tiene variantes ni inputs. Lo que hace:
45
45
 
46
46
  | dónde | regla | fuente |
47
47
  |---|---|---|
48
- | imágenes del Storage en el home y los listados del cliente | siempre con la directiva | propuesta (es donde está hoy, y donde se vio el bug del 10-07-2026: portadas grises tras un corte) |
48
+ | imágenes del Storage en el home y los listados del cliente | siempre con la directiva | canon (aprobado por el dueño, 05-10-2026, bloque B9) — es donde está hoy, y donde se vio el bug del 10-07-2026: portadas grises tras un corte |
49
49
  | el resto de `<img>` remotos | sumarla cuando la imagen sea parte de la decisión (la foto del producto en la carta) | propuesta |
50
50
 
51
51
  ## Tamaño en mobile y en web
52
52
 
53
53
  No aplica: es una directiva, no dibuja nada. El tamaño es el del `<img>`.
54
54
 
55
+ ### Por dispositivo (D16)
56
+
57
+ No aplica: no tiene `size` ni escala que repartir por dispositivo (D16, dueño 05-10-2026). El
58
+ `<img>` mide lo que diga el CSS de la pantalla o el componente que lo contiene (`sui-avatar` con
59
+ `bp.sizeAvatar()`, la tarjeta del local), y la directiva se pone igual en todos los dispositivos.
60
+
55
61
  ## API
56
62
 
57
63
  <!-- api:inicio -->
@@ -89,6 +95,49 @@ Solo se aplica a `<img>` (`selector: img[suiImgRetry]`). El `src` se pone como s
89
95
  - **No borra el cache completo** a propósito: solo la entrada de esa URL. Borrarlo todo mataría
90
96
  la ventaja de no volver a bajar nada.
91
97
  - Funciona igual con `loading="lazy"` y `decoding="async"`.
98
+ - **Con `srcset` no**: el reintento y el `fetch` van sobre `img.src`, no sobre la candidata que el
99
+ navegador eligió (`currentSrc`), así que purgaría la URL equivocada. Va un `src` único (en el home,
100
+ la portada chica de 800 px; ver `master-pedidos/src/app/core/services/state.service.ts:45`).
101
+
102
+ ## Bien y mal
103
+
104
+ Logo en las sugerencias de búsqueda
105
+
106
+ ```html
107
+ <!-- ❌ mal: listado del cliente sin la directiva; tras un corte el logo queda roto (pages/customer/favorites/favorites.html:32; la misma sugerencia del home sí la lleva) -->
108
+ <img class="favorites__suggestion-img" [src]="s.logoUrl" [alt]="s.name" loading="lazy" />
109
+ ```
110
+
111
+ ```html
112
+ <!-- ✅ bien (con las reglas de sui-img-loaded y data-sui-img-dead en el scss) -->
113
+ <img class="favorites__suggestion-img" suiImgRetry [src]="s.logoUrl" [alt]="s.name"
114
+ loading="lazy" decoding="async" />
115
+ ```
116
+
117
+ Logo del local con inicial de respaldo
118
+
119
+ ```html
120
+ <!-- ❌ mal: el (error) a mano esconde la imagen al primer fallo, sin reintento ni inicial (pages/customer/order-history/order-history.html:168) -->
121
+ <img [src]="logo" [alt]="order.tenantName ?? 'Local'" (error)="$any($event).target.style.display='none'" />
122
+ ```
123
+
124
+ ```html
125
+ <!-- ✅ bien: logo con iniciales de respaldo → sui-avatar, no la directiva -->
126
+ <sui-avatar [size]="bp.sizeAvatar('md')" [src]="logo" [initials]="storeInitial(order)"
127
+ [alt]="order.tenantName ?? 'Local'" />
128
+ ```
129
+
130
+ Portada con varias resoluciones
131
+
132
+ ```html
133
+ <!-- ❌ mal: la directiva reintenta sobre img.src; con srcset purga la candidata equivocada (lo documenta pages/customer/store-browser/store-browser.html:332) -->
134
+ <img class="fila__img" suiImgRetry [src]="store.coverThumbUrl" [srcset]="portadaSrcset(store)" [alt]="store.name" />
135
+ ```
136
+
137
+ ```html
138
+ <!-- ✅ bien: src único, del tamaño que pide la tarjeta -->
139
+ <img class="fila__img" suiImgRetry [src]="store.coverThumbUrl" [alt]="store.name" decoding="async" />
140
+ ```
92
141
 
93
142
  ## Ejemplo
94
143