@ajuarezso/sui-design-system 0.19.0 → 0.21.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/docs/INDEX.md +136 -0
- package/docs/sui-alert/README.md +229 -0
- package/docs/sui-app-store-badge/README.md +137 -0
- package/docs/sui-avatar/README.md +263 -0
- package/docs/sui-avatar-group/README.md +115 -0
- package/docs/sui-badge/README.md +229 -0
- package/docs/sui-banner/README.md +190 -0
- package/docs/sui-brand-logo/README.md +143 -0
- package/docs/sui-breadcrumb-item/README.md +96 -0
- package/docs/sui-breadcrumbs/README.md +122 -0
- package/docs/sui-button/README.md +209 -0
- package/docs/sui-button-group/README.md +178 -0
- package/docs/sui-button-group-item/README.md +102 -0
- package/docs/sui-card/README.md +168 -0
- package/docs/sui-chart-legend/README.md +140 -0
- package/docs/sui-chart-legend-dot/README.md +118 -0
- package/docs/sui-checkbox/README.md +147 -0
- package/docs/sui-checkbox-card/README.md +150 -0
- package/docs/sui-checkbox-label/README.md +143 -0
- package/docs/sui-circular-progress-bar/README.md +131 -0
- package/docs/sui-color-dot/README.md +112 -0
- package/docs/sui-color-picker/README.md +124 -0
- package/docs/sui-compact-avatar-group/README.md +103 -0
- package/docs/sui-compact-button/README.md +190 -0
- package/docs/sui-compact-select/README.md +127 -0
- package/docs/sui-compact-select-for-input/README.md +117 -0
- package/docs/sui-content-card/README.md +147 -0
- package/docs/sui-content-divider/README.md +124 -0
- package/docs/sui-content-label/README.md +148 -0
- package/docs/sui-counter-input/README.md +145 -0
- package/docs/sui-date-selector/README.md +98 -0
- package/docs/sui-datepicker/README.md +193 -0
- package/docs/sui-day-cell/README.md +118 -0
- package/docs/sui-day-label/README.md +81 -0
- package/docs/sui-divider/README.md +102 -0
- package/docs/sui-drawer-footer/README.md +127 -0
- package/docs/sui-drawer-header/README.md +140 -0
- package/docs/sui-emoji/README.md +138 -0
- package/docs/sui-empty-state/README.md +145 -0
- package/docs/sui-fancy-button/README.md +148 -0
- package/docs/sui-file-icon/README.md +103 -0
- package/docs/sui-file-upload/README.md +154 -0
- package/docs/sui-file-upload-card/README.md +129 -0
- package/docs/sui-flag/README.md +99 -0
- package/docs/sui-form-hint/README.md +124 -0
- package/docs/sui-form-label/README.md +148 -0
- package/docs/sui-icon/README.md +135 -0
- package/docs/sui-image-upload/README.md +200 -0
- package/docs/sui-image-upload-profile/README.md +145 -0
- package/docs/sui-inline-input/README.md +104 -0
- package/docs/sui-inline-select/README.md +101 -0
- package/docs/sui-input/README.md +244 -0
- package/docs/sui-integration-switch/README.md +142 -0
- package/docs/sui-kbd/README.md +97 -0
- package/docs/sui-key-icon/README.md +125 -0
- package/docs/sui-link-button/README.md +221 -0
- package/docs/sui-logo/README.md +152 -0
- package/docs/sui-notification/README.md +179 -0
- package/docs/sui-notification-footer/README.md +98 -0
- package/docs/sui-notification-tab-menu/README.md +112 -0
- package/docs/sui-otp-input/README.md +116 -0
- package/docs/sui-pagination/README.md +146 -0
- package/docs/sui-password-strength/README.md +99 -0
- package/docs/sui-payment-icon/README.md +98 -0
- package/docs/sui-period-range/README.md +93 -0
- package/docs/sui-popover/README.md +178 -0
- package/docs/sui-product-card/README.md +173 -0
- package/docs/sui-progress-bar/README.md +132 -0
- package/docs/sui-progress-bar-label/README.md +143 -0
- package/docs/sui-radio/README.md +134 -0
- package/docs/sui-radio-card/README.md +160 -0
- package/docs/sui-radio-label/README.md +137 -0
- package/docs/sui-rating/README.md +199 -0
- package/docs/sui-rich-editor/README.md +114 -0
- package/docs/sui-segmented-control/README.md +144 -0
- package/docs/sui-select/README.md +180 -0
- package/docs/sui-slider/README.md +143 -0
- package/docs/sui-social-button/README.md +158 -0
- package/docs/sui-status-badge/README.md +164 -0
- package/docs/sui-stepper/README.md +166 -0
- package/docs/sui-store-card/README.md +147 -0
- package/docs/sui-switch/README.md +146 -0
- package/docs/sui-switch-card/README.md +149 -0
- package/docs/sui-switch-label/README.md +139 -0
- package/docs/sui-tab-chips/README.md +185 -0
- package/docs/sui-tab-menu-horizontal/README.md +142 -0
- package/docs/sui-tab-menu-vertical/README.md +136 -0
- package/docs/sui-tag/README.md +145 -0
- package/docs/sui-tag-input/README.md +120 -0
- package/docs/sui-textarea/README.md +157 -0
- package/docs/sui-time-picker/README.md +237 -0
- package/docs/sui-toast/README.md +190 -0
- package/docs/sui-tooltip/README.md +201 -0
- package/fesm2022/ajuarezso-sui-design-system.mjs +466 -279
- package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ajuarezso-sui-design-system.d.ts +367 -72
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-button
|
|
3
|
+
selector: sui-button
|
|
4
|
+
resumen: Botón base para que la persona dispare una acción
|
|
5
|
+
alternativas: [sui-link-button (acción que se lee como texto), sui-compact-button (ícono de 20-28 px en filas y barras), sui-fancy-button (el CTA héroe), sui-social-button (entrar con Google/Apple), sui-button-group (opciones que se ven como un solo control)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-button
|
|
9
|
+
|
|
10
|
+
Story: [Atoms/Button/Button](https://sui-design-system.web.app/?path=/docs/atoms-button-button--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Úsalo** por defecto para cualquier acción. Se cambia a otro componente solo por una razón
|
|
15
|
+
concreta: se lee como texto dentro de un párrafo (`sui-link-button`), es un ícono suelto en
|
|
16
|
+
una fila o barra apretada (`sui-compact-button`), es login con una marca (`sui-social-button`).
|
|
17
|
+
|
|
18
|
+
**No lo uses** para:
|
|
19
|
+
|
|
20
|
+
- Navegar sin acción: un `<a>` o `sui-link-button` con `href`.
|
|
21
|
+
- Prender/apagar algo: `sui-switch` o `sui-button-group`.
|
|
22
|
+
- Mostrar carga: **no tiene `loading`**. Pon un spinner en `[data-slot=start]` y `disabled`.
|
|
23
|
+
|
|
24
|
+
## Qué variante usar
|
|
25
|
+
|
|
26
|
+
El botón tiene dos ejes: `variant` (el peso: cuánto llama la atención) y `color` (qué hace:
|
|
27
|
+
`primary` naranja de marca, `neutral`, `error` destructivo). Las combinaciones se eligen juntas.
|
|
28
|
+
|
|
29
|
+
> **La guía del DS no dice cuándo usar cada variante ni cada color.** Su página del botón
|
|
30
|
+
> muestra las 12 combinaciones, los tamaños, el estado deshabilitado y la API; no trae
|
|
31
|
+
> do/don't ni jerarquía. Así que la tabla de variantes sigue siendo **propuesta, falta
|
|
32
|
+
> aprobación**, salvo lo que ya es canon de SUI por `sui-modal-footer`.
|
|
33
|
+
>
|
|
34
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app`: 397 botones, sin contar prototipos ni
|
|
35
|
+
> vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`, `dx/*`, `master-rutina`,
|
|
36
|
+
> `master-medico`, `dev-index`). Combinaciones: 127 `stroke neutral`, 123 `filled primary`,
|
|
37
|
+
> 31 `stroke error`, 26 `ghost neutral`, 21 `filled neutral`, 15 `ghost error`, 13 `lighter`,
|
|
38
|
+
> 7 `ghost primary`, 7 `stroke primary`, 7 `filled error`, 20 con variante o color dinámico.
|
|
39
|
+
|
|
40
|
+
### Lo que sí dice la guía del DS
|
|
41
|
+
|
|
42
|
+
- **Defaults**: `filled` + `primary` + `medium`. Igual que SUI.
|
|
43
|
+
- **Tamaños**: `medium` (40), `small` (36), `xsmall` (32) y el más chico de 28 (`2xsmall` en SUI).
|
|
44
|
+
`large` y `xlarge` **no están en la guía**: son extensión de SUI.
|
|
45
|
+
- **Hermanos**: `sui-link-button` es la acción que se integra en un texto; `sui-compact-button`,
|
|
46
|
+
la de cuando falta espacio; `sui-button-group`, botones pegados en una sola línea;
|
|
47
|
+
`sui-fancy-button`, el mismo botón con un acabado más vistoso.
|
|
48
|
+
- **Dentro de un alert la acción es un link, no un `sui-button`** (ver `sui-alert`). Hoy hay 5
|
|
49
|
+
`sui-button` proyectados en alerts.
|
|
50
|
+
|
|
51
|
+
La base de la jerarquía no se inventa: la fija **`sui-modal-footer`**, que ya está en el DS y la
|
|
52
|
+
usa toda la app. Ahí el secundario está fijo en `stroke` + `neutral`, el primario es `filled`, y
|
|
53
|
+
el destructivo es el primario con `color: 'error'`. Las reglas de abajo extienden eso a
|
|
54
|
+
cualquier grupo de botones.
|
|
55
|
+
|
|
56
|
+
| variant + color | cuándo | ejemplo real de la app | fuente |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `filled` `primary` (default) | **La** acción principal del grupo. Una sola por grupo visible: dos `filled` juntos = ninguno es principal. | `pages/orders/orders.html:236` («Aceptar pedido») · `pages/customer/cart/cart.html:204` («Continuar») | ya es canon (sui-modal-footer) |
|
|
59
|
+
| `stroke` `neutral` | Toda acción secundaria: Cancelar, Volver, Editar, Ver detalle. Va a la izquierda del primario. | `pages/orders/orders.html:320` («Encargarme yo») · `pages/envios/envios.html:175` («Ver seguimiento») | ya es canon (sui-modal-footer) |
|
|
60
|
+
| `filled` `error` | **Confirmar** algo destructivo: el último paso, dentro de la confirmación. Nunca el botón que abre la confirmación. | `pages/customer/order-history/order-history.html:132` («Sí», confirma cancelar) | canon (aprobado por el dueño, 03-10-2026) |
|
|
61
|
+
| `stroke` `error` | **Abrir** una acción destructiva (lleva a la confirmación) o el «Rechazar» de un par Aprobar/Rechazar. | `pages/customer/about/about.html:76` («Eliminar cuenta») · `pages/super-admin/master-approvals/master-approvals.html:99` («Rechazar») | canon (aprobado por el dueño, 03-10-2026) |
|
|
62
|
+
| `ghost` `neutral` / `ghost` `error` | Acción terciaria sin caja: íconos de editar/borrar en filas, «Cerrar», acciones de baja jerarquía en zonas densas. | `pages/super-admin/master-gastos/master-gastos.html:80` (editar) · `pages/customer/cart/cart.html:107` («Vaciar carrito») | canon (aprobado por el dueño, 03-10-2026) |
|
|
63
|
+
| `filled` `neutral` | Salir a otra app: WhatsApp, abrir el mapa. Es la práctica de hoy (21 usos, 13 de WhatsApp), no un principio: **falta criterio**. | `pages/driver/driver-order-detail/driver-order-detail.html:515` («WhatsApp») · `:567` («Abrir mapa») | canon (aprobado por el dueño, 03-10-2026) |
|
|
64
|
+
| `ghost` `primary` | «Ver todas», «Reporte», «Detalles» en la cabecera de una card. **A discutir**: se pisa con `sui-link-button`, que según la guía es la acción que se lee como texto. | `pages/overview/components/overview-sales/overview-sales.html:5` | a discutir |
|
|
65
|
+
| `stroke` `primary` | No usar. El secundario siempre es neutral. | — | canon (aprobado por el dueño, 03-10-2026) |
|
|
66
|
+
| `lighter` (cualquier color) | No usar en botones nuevos. En `neutral` su fondo (`bg-weak-50`) es **el mismo que el de `disabled`**: se lee como apagado. Lo existente pasa a `stroke` (secundaria) o `ghost` (terciaria). | — | canon (aprobado por el dueño, 03-10-2026) |
|
|
67
|
+
|
|
68
|
+
**Usos de hoy que romperían la propuesta** (sin prototipos):
|
|
69
|
+
|
|
70
|
+
- **13 `lighter` → `stroke` o `ghost`** (7 `neutral`, 5 `error`, 1 `primary`). Ejemplos:
|
|
71
|
+
`pages/mi-negocio/mi-negocio.html:133` («Cambiar plan»), `pages/customer/cart/cart.html:33`
|
|
72
|
+
(«Quitar no disponibles»), `pages/customer/order-tracking/order-tracking.html:737` («Cancelar
|
|
73
|
+
pedido», `lighter error` → `stroke error`). 4 de los 13 son los que viven dentro de un alert
|
|
74
|
+
(`pages/overview/overview.html:41`, `pages/mi-negocio/mi-negocio.html:381`, `cart.html:33`,
|
|
75
|
+
`pages/role-selector/role-selector.html:59`): ahí la salida correcta es `actions[]` del alert.
|
|
76
|
+
- **7 `stroke primary` → `stroke neutral`.** Ejemplos: `pages/customer/checkout/checkout.html:117`
|
|
77
|
+
(«Agregar dirección de entrega»), `pages/customer/profile/profile.html:131`,
|
|
78
|
+
`pages/super-admin/master-drivers/master-drivers.html:462`.
|
|
79
|
+
- **4 `filled error` que no confirman nada destructivo (+1 a discutir):**
|
|
80
|
+
`pages/empresa/about/about.html:46` y `pages/repartidor/about/about.html:46` («Eliminar mi cuenta»
|
|
81
|
+
abre un modal → `stroke error`, como ya lo hace `pages/customer/about/about.html:76`);
|
|
82
|
+
`pages/driver/driver-dashboard/driver-dashboard.html:139` («Recargar S/ X para pagar»: urgente,
|
|
83
|
+
no destructivo → `filled primary`); `pages/super-admin/master-settings/master-settings.html:714`
|
|
84
|
+
(«Salir» → `stroke neutral`). A discutir: `driver-dashboard.html:144` («Acepto y pago S/ X» de una
|
|
85
|
+
multa) — es dinero que sale, pero no borra nada.
|
|
86
|
+
- **7 `ghost primary` a discutir** (si se decide que son links): `components/product-categories/product-categories.html:17`,
|
|
87
|
+
`pages/overview/components/overview-visitors/overview-visitors.html:5`, `pages/customer/profile/profile.html:90`.
|
|
88
|
+
- **2 `filled primary` en la misma vista:** `pages/products/add-product/add-product.html:276`
|
|
89
|
+
(«Aplicar») y `:281` («Continuar»). No se recalculó: la búsqueda fue por cercanía de líneas y
|
|
90
|
+
puede haber más.
|
|
91
|
+
|
|
92
|
+
### `size`
|
|
93
|
+
|
|
94
|
+
Uso de hoy: 123 `large`, 91 `small`, 60 `medium`, 50 `xsmall`, 29 `2xsmall`, 44 dinámicos.
|
|
95
|
+
|
|
96
|
+
| size | cuándo | fuente |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `large` (48 px) | Footers de modal y CTA táctiles en mobile. | ya es canon (default de sui-modal-footer); extensión de SUI, la guía no lo tiene |
|
|
99
|
+
| `medium` (40 px, default) | CTA en desktop y formularios. | default según la guía del DS; el «cuándo» es propuesta |
|
|
100
|
+
| `small` (36) / `xsmall` (32) | Acciones dentro de cards, filas y cabeceras de sección. | escala según la guía del DS; el «cuándo» es propuesta |
|
|
101
|
+
| `2xsmall` (28) | Acciones de ícono en tablas y filas densas. Si el espacio es de verdad chico, mirar antes `sui-compact-button`. | escala según la guía del DS; el «cuándo» es propuesta |
|
|
102
|
+
| `xlarge` (52) | Extensión de SUI. Sin uso hoy. | canon (aprobado por el dueño, 03-10-2026) |
|
|
103
|
+
|
|
104
|
+
En la app el patrón responsive ya es `[size]="bp.isMobile() ? 'large' : 'medium'"` (o
|
|
105
|
+
`bp.controlSize()`).
|
|
106
|
+
|
|
107
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
108
|
+
|
|
109
|
+
Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un
|
|
110
|
+
flujo nuevo cae en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
|
|
111
|
+
|
|
112
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `stroke` `primary` | el secundario es neutral; dos naranjas compiten por ser el principal | landings con dos CTA de marca sobre fondo oscuro |
|
|
115
|
+
| `lighter` (los 3 colores) | en `neutral` tiene el mismo fondo que `disabled`: parece apagado | botones dentro de un alert o banner teñido, donde `stroke` se ve pesado |
|
|
116
|
+
| `ghost` `primary` | lo hace mejor `sui-link-button` `primary` («Ver todas») | una acción con ícono en una barra de herramientas donde todo va sin caja |
|
|
117
|
+
| `sui-social-button` Facebook, X, LinkedIn, GitHub, Dropbox | no hay login con esas cuentas | «Continuar con Facebook» o botones de compartir en redes |
|
|
118
|
+
| `sui-social-button` `onlyIcon` | Apple exige el texto en su botón de login | una fila de «compartir en» fuera del login |
|
|
119
|
+
| `sui-app-store-badge` `alternative` | el `preferred` es el oficial de cada tienda | landings o piezas sobre fondo oscuro o de color |
|
|
120
|
+
| `sui-app-store-badge` otras tiendas | la app no está publicada ahí | Huawei AppGallery o Galaxy Store si se publica ahí |
|
|
121
|
+
| `sui-fancy-button` | estilo de marketing (brillo, degradé) | el CTA principal de una landing, «Mejorar plan» o una campaña |
|
|
122
|
+
| `sui-button-group` (0 usos) | hoy no hay opciones combinables pegadas | días del horario del local o filtros que se suman |
|
|
123
|
+
| `sui-topbar-button` (0 usos) | las barras de la app tienen sus íconos propios | si se unifica la barra superior del admin con la del DS |
|
|
124
|
+
|
|
125
|
+
## Tamaño en mobile y en web
|
|
126
|
+
|
|
127
|
+
En mobile se toca con el dedo: 40 px como mínimo y 48 en la acción principal. En web, 28–40.
|
|
128
|
+
|
|
129
|
+
| uso | mobile | web | fuente |
|
|
130
|
+
|---|---|---|---|
|
|
131
|
+
| acción principal de la pantalla o footer de modal | `large` (48) | `medium` (40) | canon (aprobado por el dueño, 03-10-2026) (footer de modal siempre `large`) |
|
|
132
|
+
| acción dentro de una card o una fila | `medium` (40) | `small` (36) / `xsmall` (32) | canon (aprobado por el dueño, 03-10-2026) |
|
|
133
|
+
| acción en una tabla densa | — (no hay tablas en mobile) | `2xsmall` (28) | canon (aprobado por el dueño, 03-10-2026) |
|
|
134
|
+
| CTA de landing | `xlarge` (52) | `large` (48) | propuesta, falta aprobación |
|
|
135
|
+
|
|
136
|
+
Patrón (la app ya lo usa en 27 lugares; `bp` = `BreakpointsService`, mobile hasta 767 px):
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<sui-button [size]="bp.isMobile() ? 'large' : 'medium'" …/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## API
|
|
143
|
+
|
|
144
|
+
<!-- api:inicio -->
|
|
145
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
146
|
+
|
|
147
|
+
| input | tipo | default |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| `variant` | `'filled' \| 'stroke' \| 'lighter' \| 'ghost'` | `'filled'` |
|
|
150
|
+
| `color` | `'primary' \| 'neutral' \| 'error'` | `'primary'` |
|
|
151
|
+
| `size` | `'2xsmall' \| 'xsmall' \| 'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
|
|
152
|
+
| `onlyIcon` | `boolean` | `false` |
|
|
153
|
+
| `disabled` | `boolean` | `false` |
|
|
154
|
+
| `leftIcon` | `string \| null` | `null` |
|
|
155
|
+
| `rightIcon` | `string \| null` | `null` |
|
|
156
|
+
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` |
|
|
157
|
+
|
|
158
|
+
Outputs: ninguno.
|
|
159
|
+
<!-- api:fin -->
|
|
160
|
+
|
|
161
|
+
El clic se escucha con `(click)` nativo sobre el host. `leftIcon` y `rightIcon` reciben el nombre
|
|
162
|
+
RemixIcon, sin el prefijo `ri-`.
|
|
163
|
+
|
|
164
|
+
Slots: `<ng-content>` (el texto), `[data-slot=start]`, `[data-slot=end]` (íconos que no son RemixIcon, spinner).
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// Exportado desde sui-button.ts; lo usan sui-modal-footer, sui-drawer-footer y el footer de filtros.
|
|
168
|
+
interface SuiFooterButtonConfig {
|
|
169
|
+
label: string;
|
|
170
|
+
disabled?: boolean;
|
|
171
|
+
color?: 'primary' | 'error'; // solo lo lee el botón primario
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Trampas
|
|
176
|
+
|
|
177
|
+
- **No es un `<button>` nativo.** El host es `<sui-button role="button">`. `type="submit"`
|
|
178
|
+
funciona porque intercepta el clic y llama a `form.requestSubmit()` del `<form>` más cercano;
|
|
179
|
+
fuera de un `<form>` no hace nada.
|
|
180
|
+
- **El output no existe**: aquí es `(click)`. En `sui-link-button`, `sui-compact-button`,
|
|
181
|
+
`sui-fancy-button` y `sui-social-button` es `(clicked)`.
|
|
182
|
+
- **La prop de peso se llama distinto en cada hermano:** `variant` aquí, `variantStyle` en
|
|
183
|
+
compact-button y social-button, `type` en fancy-button (donde el `type` HTML es `htmlType`).
|
|
184
|
+
- **`primary` es naranja (`#fa7319`)**, no el azul del Figma de origen.
|
|
185
|
+
- `onlyIcon` no define el ícono: va con `leftIcon` (o el slot `start`). Solo con `onlyIcon` el
|
|
186
|
+
botón sale vacío. Y **no hay input `ariaLabel`**: un botón de solo ícono queda sin nombre para
|
|
187
|
+
el lector de pantalla salvo que pongas `aria-label="…"` en el host a mano.
|
|
188
|
+
- El ícono mide 20 px en todos los tamaños.
|
|
189
|
+
- El radio es pill (999 px) en todos los tamaños, decisión del proyecto; el Figma de origen usa 8/10 px.
|
|
190
|
+
- `disabled` usa `pointer-events: none` y colores con `!important`: no se puede pintar un
|
|
191
|
+
disabled propio desde afuera.
|
|
192
|
+
- Ya trae el arreglo de iOS para el `:active` pegado (`touchstart`/`touchend`): no lo agregues.
|
|
193
|
+
|
|
194
|
+
## Ejemplo
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<!-- Par de acciones: secundario a la izquierda, primario a la derecha -->
|
|
198
|
+
<sui-button variant="stroke" color="neutral" (click)="cancelar()">Cancelar</sui-button>
|
|
199
|
+
<sui-button (click)="guardar()">Guardar</sui-button>
|
|
200
|
+
|
|
201
|
+
<!-- Abre una acción destructiva (la confirmación usa filled error) -->
|
|
202
|
+
<sui-button variant="stroke" color="error" leftIcon="delete-bin-line" (click)="abrirConfirmacion()">
|
|
203
|
+
Eliminar cuenta
|
|
204
|
+
</sui-button>
|
|
205
|
+
|
|
206
|
+
<!-- Solo ícono -->
|
|
207
|
+
<sui-button variant="ghost" color="neutral" size="2xsmall" leftIcon="pencil-line" [onlyIcon]="true"
|
|
208
|
+
aria-label="Editar" (click)="editar()" />
|
|
209
|
+
```
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-button-group
|
|
3
|
+
selector: sui-button-group
|
|
4
|
+
resumen: Opciones pegadas en una fila que se prenden y se apagan, varias a la vez
|
|
5
|
+
alternativas: [sui-segmented-control (cambiar de vista o elegir una sola opción), sui-checkbox (opciones que se suman dentro de un formulario largo), sui-tab-chips (filtros en chips que se pueden desplazar)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-button-group
|
|
9
|
+
|
|
10
|
+
Story: [Atoms/Button/Button Group](https://sui-design-system.web.app/?path=/docs/atoms-button-button-group--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Hoy no se usa** (0 usos en la app).
|
|
15
|
+
|
|
16
|
+
**Úsalo** para elegir **varias** opciones cortas que se combinan y se ven juntas: los días de
|
|
17
|
+
atención de un local (L M M J V S D), filtros que se suman en una barra.
|
|
18
|
+
|
|
19
|
+
**No lo uses** para:
|
|
20
|
+
|
|
21
|
+
- **Cambiar de vista** (lista / mapa, día / semana / mes) o elegir **una sola** opción:
|
|
22
|
+
`sui-segmented-control`.
|
|
23
|
+
- **Acciones** (guardar, exportar): son `sui-button` sueltos; el grupo marca qué está prendido,
|
|
24
|
+
no dispara acciones.
|
|
25
|
+
- **Opciones largas** o que no entran en una fila del teléfono: `sui-checkbox` en lista.
|
|
26
|
+
|
|
27
|
+
## Qué variante usar
|
|
28
|
+
|
|
29
|
+
> **La guía del DS no dice cuándo usarlo**: lo define como «botones pegados en una línea
|
|
30
|
+
> horizontal». **Su ejemplo es «Grid view / List view / Gallery view», o sea cambiar de vista, y
|
|
31
|
+
> el dueño decidió lo contrario el 03-10-2026: cambiar de vista va con `sui-segmented-control`.**
|
|
32
|
+
> El button-group queda para elegir varias opciones a la vez. Las reglas de abajo son **canon
|
|
33
|
+
> (aprobado por el dueño, 03-10-2026)** salvo donde dice otra fuente.
|
|
34
|
+
>
|
|
35
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0** button-groups, sin contar
|
|
36
|
+
> prototipos ni vitrinas.
|
|
37
|
+
|
|
38
|
+
### Lo que sí dice la guía del DS
|
|
39
|
+
|
|
40
|
+
- **Para qué es**: un conjunto de botones pegados en una línea horizontal.
|
|
41
|
+
- **Tamaños**: `small` (default), `xsmall` y el más chico (`xxsmall` en la guía, `2xsmall` en SUI).
|
|
42
|
+
- **La selección no es del componente**: la guía la arma con un toggle group aparte. En SUI viene
|
|
43
|
+
incluida (`multi`, `[(value)]`, `change`) y es extensión de SUI.
|
|
44
|
+
|
|
45
|
+
### `multi`
|
|
46
|
+
|
|
47
|
+
| multi | cuándo | ejemplo de copy | fuente |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `true` | Elegir días u opciones que se combinan. | L M M J V S D (días de atención) | canon (aprobado por el dueño, 03-10-2026) |
|
|
50
|
+
| `false` (default) | No: elegir una sola o cambiar de vista va con `sui-segmented-control`. | — | cambiar de vista: canon (aprobado por el dueño, 03-10-2026) · elegir una sola en general: propuesta |
|
|
51
|
+
|
|
52
|
+
### `size`
|
|
53
|
+
|
|
54
|
+
| size | alto | cuándo | fuente |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| `large` | 48 | Mobile: los días del horario y las opciones de un formulario, para tocar con el dedo. Label/Medium (16/24), ícono 20, radius 12. | canon (aprobado por el dueño, 03-10-2026) · extensión SUI |
|
|
57
|
+
| `medium` | 40 | Mobile cuando la fila es larga (los 7 días) y `large` no entra en 360 px. Label/Small (14/20), ícono 20, radius 10. | alto y tipografía: canon (aprobado por el dueño, 03-10-2026), extensión SUI · cuándo: propuesta |
|
|
58
|
+
| `small` (default) | 36 | Web: los días del horario, formularios. | escala según la guía del DS · cuándo: propuesta |
|
|
59
|
+
| `xsmall` | 32 | Barras de filtros en desktop. | escala según la guía del DS · cuándo: propuesta |
|
|
60
|
+
| `2xsmall` | 24 | Solo desktop, dentro de una tabla o una card densa. Muy chico para el dedo. | escala según la guía del DS · cuándo: propuesta |
|
|
61
|
+
|
|
62
|
+
### `items` (lo que lleva cada opción)
|
|
63
|
+
|
|
64
|
+
| contenido | cuándo | fuente |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| solo `label` | Lo normal: textos de una a tres letras o una palabra («L», «Delivery»). | propuesta |
|
|
67
|
+
| `leftIcon` + `label` | Cuando el ícono ayuda a distinguir opciones parecidas (moto / bici / auto). | propuesta |
|
|
68
|
+
| solo ícono (`leftIcon`, sin `label`) | Desktop, opciones universales; **`ariaLabel` obligatorio** en cada una. | propuesta |
|
|
69
|
+
| `disabled: true` en una opción | La opción existe pero hoy no se puede elegir (un día que el local no atiende por regla). | propuesta |
|
|
70
|
+
| cuántas | De 2 a 7. Con más, no entra en el ancho del teléfono: lista de `sui-checkbox`. | propuesta (el Figma dibuja de 2 a 6; los días son 7) |
|
|
71
|
+
|
|
72
|
+
### `value`, `ariaLabel`, `change`
|
|
73
|
+
|
|
74
|
+
| prop | cuándo | fuente |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `[(value)]` | Con `multi`, un `string[]` con los `value` prendidos. | propuesta |
|
|
77
|
+
| `ariaLabel` | Siempre: dice qué se elige («Días de atención»). El host es `role="group"`. | propuesta |
|
|
78
|
+
| `(change)` | Cuando hay que reaccionar al cambio (guardar, filtrar); si alcanza con el `model`, no hace falta. | propuesta |
|
|
79
|
+
|
|
80
|
+
## Tamaño en mobile y en web
|
|
81
|
+
|
|
82
|
+
En mobile se toca con el dedo: el área táctil tiene que medir **44 px o más**. En web, con mouse,
|
|
83
|
+
va un tamaño más chico. La app ya lo resuelve con `BreakpointsService`
|
|
84
|
+
(`src/app/services/breakpoints.service.ts`, mobile = hasta 767 px):
|
|
85
|
+
`[size]="bp.isMobile() ? 'large' : 'small'"`, 27 usos hoy en `master-pedidos` (16 `large`/`small`,
|
|
86
|
+
9 `large`/`medium`, 2 `medium`/`xsmall`). Regla del dueño, 03-10-2026.
|
|
87
|
+
|
|
88
|
+
Desde el 03-10-2026 el grupo tiene `large` (48) y `medium` (40), la misma escala de `sui-button`,
|
|
89
|
+
así que el par es el de toda la app.
|
|
90
|
+
|
|
91
|
+
| uso | mobile | web | fuente |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| días del horario, opciones en un formulario | `large` (48) | `small` (36) | canon (aprobado por el dueño, 03-10-2026) |
|
|
94
|
+
| fila larga que no entra a 48 en 360 px (los 7 días con texto) | `medium` (40) | `small` (36) | propuesta, falta aprobación |
|
|
95
|
+
| barra de filtros | `large` | `xsmall` (32) | propuesta, falta aprobación |
|
|
96
|
+
| dentro de una tabla o una card densa | no va en mobile | `2xsmall` (24) | propuesta |
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<sui-button-group [items]="dias" [multi]="true" [(value)]="diasAbiertos" ariaLabel="Días de atención"
|
|
100
|
+
[size]="bp.isMobile() ? 'large' : 'small'" />
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
En mobile, cada opción tiene que ser ancha (los 7 días repartidos en todo el ancho, no apretados a
|
|
104
|
+
la izquierda). Si las opciones no entran ni en `medium`, va una lista de `sui-checkbox`.
|
|
105
|
+
|
|
106
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
107
|
+
|
|
108
|
+
Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
|
|
109
|
+
en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
|
|
110
|
+
|
|
111
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `sui-button-group` (0 usos) | hoy no hay opciones combinables pegadas | días del horario del local (L M M J V S D) o filtros que se suman |
|
|
114
|
+
| `multi` en `false` (una sola opción) | cambiar de vista y elegir una sola van con `sui-segmented-control` | una barra de herramientas de desktop donde el resto ya son button-groups y una opción es excluyente |
|
|
115
|
+
|
|
116
|
+
## API
|
|
117
|
+
|
|
118
|
+
<!-- api:inicio -->
|
|
119
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
120
|
+
|
|
121
|
+
| input | tipo | default |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `items` | `readonly SuiButtonGroupEntry[]` | — (requerido) |
|
|
124
|
+
| `size` | `'small' \| 'xsmall' \| '2xsmall' \| 'medium' \| 'large'` | `'small'` |
|
|
125
|
+
| `multi` | `boolean` | `false` |
|
|
126
|
+
| `value` · model, `[(value)]` | `string \| string[]` | `''` |
|
|
127
|
+
| `ariaLabel` | `string` | `''` |
|
|
128
|
+
|
|
129
|
+
| output | payload |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `change` | `string \| string[]` |
|
|
132
|
+
<!-- api:fin -->
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
interface SuiButtonGroupEntry {
|
|
136
|
+
value: string; // único: es lo que se guarda en value
|
|
137
|
+
label?: string;
|
|
138
|
+
leftIcon?: string; // RemixIcon, sin el prefijo ri-
|
|
139
|
+
rightIcon?: string;
|
|
140
|
+
disabled?: boolean;
|
|
141
|
+
ariaLabel?: string; // obligatorio en la práctica si no hay label
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Trampas
|
|
146
|
+
|
|
147
|
+
- **En una sola opción (`multi` en `false`) no se puede apagar** la que está prendida: tocarla otra
|
|
148
|
+
vez no hace nada. Es el comportamiento de un segmented control, otra razón para usar ese.
|
|
149
|
+
- **`value` cambia de tipo con `multi`**: `string` en una sola, `string[]` en varias, y el
|
|
150
|
+
`model` es `string | string[]`. El signal que se enlaza con `[(value)]` tiene que ser
|
|
151
|
+
`signal<string | string[]>` (con `signal<string[]>` no compila); al leerlo, estrecha con
|
|
152
|
+
`Array.isArray`. El default es `''`: con `multi`, arráncalo en `[]` o en un arreglo.
|
|
153
|
+
- **Dos opciones con el mismo texto** (los dos «M» de martes y miércoles) se distinguen solo por
|
|
154
|
+
`value`; ponles `ariaLabel` con el nombre completo.
|
|
155
|
+
- **El output es `(change)`**, no `(clicked)` ni `(valueChange)` a mano: `[(value)]` ya usa el
|
|
156
|
+
`valueChange` del `model`.
|
|
157
|
+
- Las opciones van por `items`, no proyectando `sui-button-group-item` adentro: el grupo es quien
|
|
158
|
+
pone las puntas redondeadas y junta los bordes.
|
|
159
|
+
- El estado prendido es sutil (fondo gris y texto más oscuro), no naranja. En un formulario,
|
|
160
|
+
acompáñalo con un texto que diga cuántas hay elegidas.
|
|
161
|
+
- Ya trae el arreglo de iOS para el `:active` pegado: no lo agregues.
|
|
162
|
+
|
|
163
|
+
## Ejemplo
|
|
164
|
+
|
|
165
|
+
```html
|
|
166
|
+
<!-- Días de atención del local -->
|
|
167
|
+
<sui-button-group [items]="dias" [multi]="true" [(value)]="diasAbiertos" ariaLabel="Días de atención" />
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
readonly dias: SuiButtonGroupEntry[] = [
|
|
172
|
+
{ value: 'lu', label: 'L', ariaLabel: 'Lunes' }, { value: 'ma', label: 'M', ariaLabel: 'Martes' },
|
|
173
|
+
{ value: 'mi', label: 'M', ariaLabel: 'Miércoles' }, { value: 'ju', label: 'J', ariaLabel: 'Jueves' },
|
|
174
|
+
{ value: 'vi', label: 'V', ariaLabel: 'Viernes' }, { value: 'sa', label: 'S', ariaLabel: 'Sábado' },
|
|
175
|
+
{ value: 'do', label: 'D', ariaLabel: 'Domingo' },
|
|
176
|
+
];
|
|
177
|
+
readonly diasAbiertos = signal<string | string[]>(['lu', 'ma', 'mi', 'ju', 'vi']);
|
|
178
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-button-group-item
|
|
3
|
+
selector: sui-button-group-item
|
|
4
|
+
resumen: Cada opción de un sui-button-group; no se usa suelto
|
|
5
|
+
alternativas: [sui-button-group (el grupo, que ya pinta sus ítems desde items[]), sui-segmented-control (elegir una sola opción), sui-switch (prender o apagar algo suelto)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-button-group-item
|
|
9
|
+
|
|
10
|
+
Story: [Atoms/Button/Button Group Item](https://sui-design-system.web.app/?path=/docs/atoms-button-button-group-item--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**No lo uses directo.** `sui-button-group` lo pinta solo a partir de su `items[]`: el grupo le pasa
|
|
15
|
+
`size`, `active` y `disabled`, y es quien le redondea las puntas y le junta los bordes. Las reglas de
|
|
16
|
+
uso (cuándo un button-group, qué tamaño, qué lleva cada opción) están en el README de
|
|
17
|
+
`sui-button-group`.
|
|
18
|
+
|
|
19
|
+
**Suelto** mantiene su propio borde y se ve como un botón de alternar aislado. Para prender o
|
|
20
|
+
apagar algo suelto está `sui-switch`; para una acción, `sui-button`.
|
|
21
|
+
|
|
22
|
+
## Qué variante usar
|
|
23
|
+
|
|
24
|
+
> **La guía del DS lo trata como parte del grupo** («ButtonGroup.Item»), sin props propias más
|
|
25
|
+
> que la de componer. Hoy hay **0** usos directos en la app, ni del grupo.
|
|
26
|
+
|
|
27
|
+
| prop | valor | cuándo | fuente |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| `size` | `large` · `medium` · `small` · `xsmall` · `2xsmall` | El mismo del grupo; lo pone el grupo. Altos 48 · 40 · 36 · 32 · 24. | `small` a `2xsmall`: escala según la guía del DS · `large` y `medium`: canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
30
|
+
| `active` | `true` | La opción está elegida; lo calcula el grupo desde `value`. | propuesta |
|
|
31
|
+
| `disabled` | `true` | La opción existe pero hoy no se puede elegir; viene de `disabled` en la entrada de `items`. | propuesta |
|
|
32
|
+
| `label` | texto | Una a tres letras o una palabra. | propuesta |
|
|
33
|
+
| `leftIcon` / `rightIcon` | RemixIcon | Ícono a la izquierda si ayuda a distinguir; a la derecha, sin caso. Sin `label` queda cuadrado. | propuesta |
|
|
34
|
+
| `ariaLabel` | texto | Obligatorio si no hay `label`, o si dos opciones tienen el mismo texto. | propuesta |
|
|
35
|
+
|
|
36
|
+
## Tamaño en mobile y en web
|
|
37
|
+
|
|
38
|
+
En mobile se toca con el dedo: el área táctil tiene que medir **44 px o más**. En web, con mouse,
|
|
39
|
+
va un tamaño más chico. La app ya lo resuelve con `BreakpointsService`
|
|
40
|
+
(`src/app/services/breakpoints.service.ts`, mobile = hasta 767 px):
|
|
41
|
+
`[size]="bp.isMobile() ? 'large' : 'small'"`, 27 usos hoy en `master-pedidos` (16 `large`/`small`,
|
|
42
|
+
9 `large`/`medium`, 2 `medium`/`xsmall`). Regla del dueño, 03-10-2026.
|
|
43
|
+
|
|
44
|
+
El tamaño lo pone el grupo: es el mismo `size` de `sui-button-group` (ver su README).
|
|
45
|
+
|
|
46
|
+
| uso | mobile | web | fuente |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| dentro de un grupo | `large` (48) · `medium` (40) si la fila no entra | `small` (36) | canon (aprobado por el dueño, 03-10-2026) |
|
|
49
|
+
| suelto (barra de herramientas) | no va en mobile | `xsmall` o `2xsmall` | propuesta |
|
|
50
|
+
|
|
51
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
52
|
+
|
|
53
|
+
Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
|
|
54
|
+
en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
|
|
55
|
+
|
|
56
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `sui-button-group-item` suelto | el grupo ya lo pinta desde `items[]`, y suelto se lee como un switch mal dibujado | un botón de alternar en una barra de herramientas de desktop (negrita en un editor de texto) |
|
|
59
|
+
| `rightIcon` | en un grupo de opciones cortas, un ícono a la derecha no agrega nada | una opción que abre un menú (con `arrow-down-s-line`) |
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
<!-- api:inicio -->
|
|
64
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
65
|
+
|
|
66
|
+
| input | tipo | default |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `label` | `string` | `''` |
|
|
69
|
+
| `leftIcon` | `string` | `''` |
|
|
70
|
+
| `rightIcon` | `string` | `''` |
|
|
71
|
+
| `size` | `'small' \| 'xsmall' \| '2xsmall' \| 'medium' \| 'large'` | `'small'` |
|
|
72
|
+
| `active` | `boolean` | `false` |
|
|
73
|
+
| `disabled` | `boolean` | `false` |
|
|
74
|
+
| `ariaLabel` | `string` | `''` |
|
|
75
|
+
|
|
76
|
+
| output | payload |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `clicked` | `void` |
|
|
79
|
+
<!-- api:fin -->
|
|
80
|
+
|
|
81
|
+
## Trampas
|
|
82
|
+
|
|
83
|
+
- **El output es `(clicked)` y no lleva payload** (`void`), a diferencia de los otros botones, que
|
|
84
|
+
mandan el `MouseEvent`.
|
|
85
|
+
- **`active` no se prende solo al tocarlo**: el ítem solo avisa; quien lo usa (normalmente el grupo)
|
|
86
|
+
tiene que cambiar `active`.
|
|
87
|
+
- Los íconos miden 20 en `small` y `xsmall`, y 16 en `2xsmall`.
|
|
88
|
+
- Suelto no recibe las puntas redondeadas del grupo: esas reglas dependen de los atributos
|
|
89
|
+
`data-sui-first` y `data-sui-last` que pone `sui-button-group`.
|
|
90
|
+
- Es un `<button type="button">` con `aria-pressed`: el lector de pantalla lo anuncia como botón de
|
|
91
|
+
alternar.
|
|
92
|
+
|
|
93
|
+
## Ejemplo
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<!-- Lo normal: no se escribe a mano, lo arma el grupo -->
|
|
97
|
+
<sui-button-group [items]="dias" [multi]="true" [(value)]="diasAbiertos" ariaLabel="Días de atención" />
|
|
98
|
+
|
|
99
|
+
<!-- Suelto, solo en el caso de «cuándo sí» -->
|
|
100
|
+
<sui-button-group-item leftIcon="bold" ariaLabel="Negrita" size="xsmall"
|
|
101
|
+
[active]="negrita()" (clicked)="negrita.set(!negrita())" />
|
|
102
|
+
```
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-card
|
|
3
|
+
selector: sui-card
|
|
4
|
+
resumen: Superficie con borde, radio y sombra que agrupa contenido, con slots de header, cuerpo, media y footer
|
|
5
|
+
alternativas: [sui-content-card (una fila «cara + nombre + detalle» dentro de su cajita), sui-radio-card / sui-checkbox-card / sui-switch-card (la card ES la opción que se elige), sui-product-card (un producto de la carta), sui-store-card (un local en un listado del cliente), sui-modal-sheet (contenido encima de la pantalla)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-card
|
|
9
|
+
|
|
10
|
+
Story: [Molecules/Card/Card](https://sui-design-system.web.app/?path=/docs/molecules-card-card--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Úsalo** como la superficie de una sección o de un ítem: un bloque de KPIs, un gráfico con su
|
|
15
|
+
título, una fila de lista que se toca, un formulario corto dentro de una página.
|
|
16
|
+
|
|
17
|
+
**No lo uses** para:
|
|
18
|
+
|
|
19
|
+
- Una fila «foto o ícono + nombre + detalle»: eso es `sui-content-card` (o `sui-content-label`
|
|
20
|
+
dentro de una card que ya existe).
|
|
21
|
+
- Una opción que se elige: `sui-radio-card`, `sui-checkbox-card` o `sui-switch-card` ya traen la
|
|
22
|
+
card, el estado elegido y el área táctil.
|
|
23
|
+
- Un producto de la carta o un local del listado del cliente: `sui-product-card` y
|
|
24
|
+
`sui-store-card`.
|
|
25
|
+
- Contenido que aparece encima de la pantalla: `sui-modal` / `sui-modal-sheet`.
|
|
26
|
+
|
|
27
|
+
Regla corta: **agrupa contenido → card · es una fila con cara → content-card · se elige → card de
|
|
28
|
+
selección.**
|
|
29
|
+
|
|
30
|
+
## Qué variante usar
|
|
31
|
+
|
|
32
|
+
> **La guía del DS no tiene página de la card.** Nada de lo que sigue sale de una guía: lo
|
|
33
|
+
> marcado «propuesta» sale de cómo la usa la app y falta el ok del dueño; lo «canon» ya está
|
|
34
|
+
> aprobado.
|
|
35
|
+
>
|
|
36
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app` (sin `master-preview`,
|
|
37
|
+
> `master-prototipos`, `mockup-*`, `dx/*`, `_guia-*`, `master-rutina`, `master-medico`,
|
|
38
|
+
> `master-ejercicios`): **253 cards en 79 archivos**. Variantes: 227 `default` explícito, 24 sin
|
|
39
|
+
> `variant` (también default), 2 `elevated`, 0 `flat`. Tamaños: 95 `small`, 123 `medium`
|
|
40
|
+
> explícito + 21 sin `size`, 14 `large`, 0 `xlarge`. 9 con `[interactive]`. Slots: 23
|
|
41
|
+
> `sui-card-header`, 11 `sui-card-footer`, 4 `sui-card-media`.
|
|
42
|
+
|
|
43
|
+
### `variant`
|
|
44
|
+
|
|
45
|
+
| variant | cuándo | fuente |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `default` | Lo normal: la card sobre el fondo de la app (borde + sombra x-small). 251 de 253. | propuesta (práctica de la app) |
|
|
48
|
+
| `elevated` | La card flota sobre un mapa, una foto o un fondo de color, donde el borde se pierde y hace falta la sombra media. Precedente: el seguimiento público del pedido (`pages/public/pedido-en-vivo/pedido-en-vivo.html:19`, `:69`). | propuesta |
|
|
49
|
+
| `flat` | Ver «No usar». | propuesta |
|
|
50
|
+
|
|
51
|
+
### `size` (padding × gap entre slots)
|
|
52
|
+
|
|
53
|
+
| size | medidas | cuándo | fuente |
|
|
54
|
+
|---|---|---|---|
|
|
55
|
+
| `small` | 16 / 12 | Filas de una lista de cards, KPIs chicos, cards tocables. **Toda card en mobile.** | propuesta |
|
|
56
|
+
| `medium` (default) | 20 / 16 | Secciones en web. | propuesta |
|
|
57
|
+
| `large` | 24 / 20 | La sección principal de una pantalla de web (los 14 de hoy son gráficos de reportes y los pasos del onboarding). | propuesta |
|
|
58
|
+
| `xlarge` | 28 / 24 | Ver «No usar». Extensión SUI (no está en Figma). | propuesta |
|
|
59
|
+
|
|
60
|
+
### `interactive`
|
|
61
|
+
|
|
62
|
+
| valor | cuándo | fuente |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `true` | La card entera es **una** acción: abre un detalle o lleva a otra pantalla («Mis direcciones», una factura). Da cursor, hover, foco y el arreglo de iOS. | propuesta |
|
|
65
|
+
| `false` (default) | La card tiene 2 acciones o más adentro, o ninguna. Las acciones van como botones dentro. | propuesta |
|
|
66
|
+
|
|
67
|
+
### Slots
|
|
68
|
+
|
|
69
|
+
| slot | cuándo | fuente |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `[sui-card-header]` | Título de la card a la izquierda y, si hace falta, una acción chica a la derecha (`sui-link-button` «Ver todo»). | propuesta |
|
|
72
|
+
| cuerpo (sin atributo) | El contenido. **Con 2 bloques o más, envuélvelos en un div propio con `display:flex; flex-direction:column; gap`** (ver Trampas). | canon (memoria del proyecto, 10-05-2026) |
|
|
73
|
+
| `[sui-card-media]` | Gráfico, mapa o foto de borde a borde, al final. Si después va un footer, la media deja de comerse el borde inferior. | propuesta |
|
|
74
|
+
| `[sui-card-footer]` | La acción de la card o un resumen. Si son dos botones, siguen el orden de `sui-modal-footer` (secundario a la izquierda, primario a la derecha). | canon (orden de botones, CLAUDE.md) |
|
|
75
|
+
|
|
76
|
+
**Usos de hoy que rompen las reglas:**
|
|
77
|
+
|
|
78
|
+
- **18 cards en 6 archivos con `gap` puesto en la clase del `sui-card`**, que no llega a los hijos
|
|
79
|
+
del cuerpo: `pages/central/central-inicio/central-inicio.html:36` (×6),
|
|
80
|
+
`pages/super-admin/master-semanas/master-semanas.html:21` (×4),
|
|
81
|
+
`pages/central/central-ingresos/central-ingresos.html:36` (×3), `pages/invoices/invoices.html:19`
|
|
82
|
+
(×2), `pages/driver/driver-history/driver-history.html:17` (×2) y
|
|
83
|
+
`pages/driver/driver-wallet/driver-wallet.html:154` (ahí además el `flex-direction: row` del host
|
|
84
|
+
no pone el badge al lado del monto: el cuerpo es un solo hijo).
|
|
85
|
+
- **3 cards tocables armadas a mano** (`(click)` o `routerLink` sin `[interactive]`): sin foco, sin
|
|
86
|
+
hover y sin el arreglo de iOS. `pages/super-admin/master-users/master-users.html:156` (con
|
|
87
|
+
`role="button"` y `touchstart` a mano), `pages/super-admin/master-settings/master-settings.html:601`,
|
|
88
|
+
`pages/driver/driver-dashboard/driver-dashboard.html:990`.
|
|
89
|
+
|
|
90
|
+
## Tamaño en mobile y en web
|
|
91
|
+
|
|
92
|
+
El `size` de la card es su padding, no un área táctil: no tiene que llegar a 44. Lo que importa en
|
|
93
|
+
mobile es no perder ancho.
|
|
94
|
+
|
|
95
|
+
| uso | mobile | web |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| sección, KPI, gráfico | `small` (16) | `medium` (20) |
|
|
98
|
+
| sección principal de un reporte | `small` | `large` (24) |
|
|
99
|
+
| fila de lista tocable | `small` | `small` |
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<sui-card [size]="bp.isMobile() ? 'small' : 'medium'"> … </sui-card>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`bp` es el `BreakpointsService` de la app. Va al ritmo de los gaps de página (12 / 16 / 20 por
|
|
106
|
+
breakpoint). Si la card es tocable, el alto de la fila (≥ 44) lo da su contenido, no el `size`.
|
|
107
|
+
|
|
108
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
109
|
+
|
|
110
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `variant="flat"` (0 usos) | la app siempre pone la card sobre el fondo gris, donde la sombra ayuda | una card dentro de otra superficie blanca (un sheet, otra card), donde la sombra dobla el borde |
|
|
113
|
+
| `size="xlarge"` (0 usos) | ninguna pantalla lo pide; no está en Figma | un hero de desktop (la portada de un reporte impreso) |
|
|
114
|
+
|
|
115
|
+
## API
|
|
116
|
+
|
|
117
|
+
<!-- api:inicio -->
|
|
118
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
119
|
+
|
|
120
|
+
| input | tipo | default |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `variant` | `'default' \| 'elevated' \| 'flat'` | `'default'` |
|
|
123
|
+
| `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
|
|
124
|
+
| `interactive` | `boolean` | `false` |
|
|
125
|
+
|
|
126
|
+
Outputs: ninguno.
|
|
127
|
+
<!-- api:fin -->
|
|
128
|
+
|
|
129
|
+
Slots: `[sui-card-header]`, cuerpo (`<ng-content>`), `[sui-card-media]`, `[sui-card-footer]`.
|
|
130
|
+
|
|
131
|
+
## Trampas
|
|
132
|
+
|
|
133
|
+
- **El `gap` del host separa slots, no hijos.** El cuerpo va dentro de `.sui-card__body` (flex en
|
|
134
|
+
columna, sin gap). Poner `gap` en una clase del `sui-card` no separa nada de lo que proyectas: se
|
|
135
|
+
necesita un wrapper propio (`<div class="x__card-body">` con flex y gap).
|
|
136
|
+
- Sin header ni footer el host queda con `gap: 0` (`:has(> .sui-card__body:only-child)`).
|
|
137
|
+
- **`interactive` no trae output, pero sí teclado** (desde 03-10-2026): pone `role="button"`,
|
|
138
|
+
`tabindex="0"`, hover y foco, y **Enter / Espacio hacen click en la card**, así que tu `(click)`
|
|
139
|
+
(o el `routerLink` de un `<a>` que la envuelve) también responde al teclado. No agregues
|
|
140
|
+
`(keydown.enter)` además: se dispararía dos veces. Una tecla dentro de un campo o botón
|
|
141
|
+
proyectado es de ese control, no de la card.
|
|
142
|
+
- Los slots son **atributos** (`sui-card-header`), no `slot="…"`.
|
|
143
|
+
- El host tiene `overflow: hidden` y radio 16: lo que sobresalga (un badge colgado de la esquina)
|
|
144
|
+
se corta.
|
|
145
|
+
- `sui-card-media` rompe el padding con margen negativo leído de `--sui-card-padding-x`; si le
|
|
146
|
+
cambias el padding a la card desde afuera, la media queda corrida.
|
|
147
|
+
- En modo oscuro `elevated` usa `--sui-card-elevated-border` para el borde (la sombra no se ve).
|
|
148
|
+
|
|
149
|
+
## Ejemplo
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<!-- Sección con título, dos bloques y una acción -->
|
|
153
|
+
<sui-card [size]="bp.isMobile() ? 'small' : 'medium'">
|
|
154
|
+
<div sui-card-header class="ganancias__head">
|
|
155
|
+
<h3>Tus días del mes</h3>
|
|
156
|
+
<sui-link-button variant="primary" size="small" (click)="verTodo()">Ver todo</sui-link-button>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="ganancias__card-body"> <!-- display:flex; flex-direction:column; gap:12px -->
|
|
159
|
+
<span>Martes 3 · 6 entregas · S/ 42.00</span>
|
|
160
|
+
<span>Miércoles 4 · 4 entregas · S/ 28.50</span>
|
|
161
|
+
</div>
|
|
162
|
+
</sui-card>
|
|
163
|
+
|
|
164
|
+
<!-- Fila tocable -->
|
|
165
|
+
<sui-card size="small" [interactive]="true" (click)="abrirDirecciones()">
|
|
166
|
+
…
|
|
167
|
+
</sui-card>
|
|
168
|
+
```
|