energy-foundations 2.4.0 → 2.5.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/energy-foundations.css +1 -1
- package/dist/foundations/_grid.scss +19 -55
- package/dist/generated/icon-layers.js +1 -1
- package/dist/generated/icon-layers.ts +1 -1
- package/dist/generated/icon-names.js +1 -1
- package/dist/generated/icon-names.ts +1 -1
- package/dist/index.css +238 -51
- package/dist/mixins/_breakpoint-fixed.scss +26 -0
- package/dist/mixins/_layout.scss +176 -13
- package/dist/mixins/_typography.scss +23 -8
- package/dist/style.css +238 -51
- package/dist/variables/_grid.scss +1 -1
- package/dist/variables/_typography.scss +7 -7
- package/dist/variables.css +5 -5
- package/llms/00-installation.md +125 -55
- package/llms/01-classes.md +18 -20
- package/llms/02-css-variables.md +5 -5
- package/llms/03-sass-variables-and-mixins.md +18 -0
- package/llms/04-layouts.md +437 -0
- package/llms/index.md +3 -0
- package/package.json +6 -4
- package/src/foundations/_grid.scss +19 -55
- package/src/generated/icon-layers.js +1 -1
- package/src/generated/icon-layers.ts +1 -1
- package/src/generated/icon-names.js +1 -1
- package/src/generated/icon-names.ts +1 -1
- package/src/mixins/_breakpoint-fixed.scss +26 -0
- package/src/mixins/_layout.scss +176 -13
- package/src/mixins/_typography.scss +23 -8
- package/src/variables/_grid.scss +1 -1
- package/src/variables/_typography.scss +7 -7
- package/src/variables.css +5 -5
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# Layouts A/B, Grid e integración Vue/AEM
|
|
2
|
+
|
|
3
|
+
Layout A es el valor por defecto y Layout B es editorial y opt-in. No migres portales existentes ni cambies sus breakpoints sin una petición explícita.
|
|
4
|
+
|
|
5
|
+
## Contrato de página
|
|
6
|
+
|
|
7
|
+
| Perfil | Contexto tipográfico | Contenedor | Grid |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| A | body sin atributo data-rds-layout | .rds-layout-max o @include rds.layout-max (contextual) | .rds-grid |
|
|
10
|
+
| B | body[data-rds-layout="b"] | .rds-layout-max o @include rds.layout-max (contextual) | .rds-grid (contextual) |
|
|
11
|
+
|
|
12
|
+
El contenedor .rds-layout-max y el grid .rds-grid siguen automáticamente el atributo del body. No existe contenedor ni clase de grid editorial local. Los helpers rds-col-* y Flex responsive siguen usando los cortes de A; no los trates como helpers B.
|
|
13
|
+
|
|
14
|
+
Solo la página o el shell controla el atributo global. Dos perfiles tipográficos simultáneos requieren un contexto local diseñado expresamente; no alternes el body desde componentes individuales.
|
|
15
|
+
|
|
16
|
+
## API y breakpoints
|
|
17
|
+
|
|
18
|
+
### Breakpoints Sass
|
|
19
|
+
|
|
20
|
+
A Layout A Sin atributo en el body (por defecto)
|
|
21
|
+
`mobile`: max-width: 719px
|
|
22
|
+
`tablet`: min-width: 720px
|
|
23
|
+
`desktop`: min-width: 1280px
|
|
24
|
+
`desktop-large`: min-width: 1440px
|
|
25
|
+
`ultrawide`: min-width: 1600px
|
|
26
|
+
B Layout B Con body[data-rds-layout="b"]
|
|
27
|
+
`mobile`: max-width: 799px
|
|
28
|
+
`tablet`: min-width: 800px
|
|
29
|
+
`desktop`: min-width: 1024px
|
|
30
|
+
`desktop-large`: min-width: 1250px
|
|
31
|
+
`ultrawide`: min-width: 1584px
|
|
32
|
+
|
|
33
|
+
Los nombres son los mismos en ambos layouts: el atributo del `body` decide qué valor se aplica.
|
|
34
|
+
|
|
35
|
+
```scss
|
|
36
|
+
.component {
|
|
37
|
+
// Mobile first
|
|
38
|
+
|
|
39
|
+
@include breakpoint(tablet) {
|
|
40
|
+
// Layout A desde 720px y Layout B desde 800px, según el body
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Breakpoints contextuales
|
|
46
|
+
|
|
47
|
+
`breakpoint` siempre tiene en cuenta el layout declarado en el `body` para los nombres semánticos (`mobile`, `tablet`, `desktop`, `desktop-large` y `ultrawide`). No hay parámetros ni configuración que activar:
|
|
48
|
+
|
|
49
|
+
```scss
|
|
50
|
+
@use 'energy-foundations/src/rds-variables' as rds;
|
|
51
|
+
|
|
52
|
+
.card {
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
|
|
55
|
+
@include rds.breakpoint(desktop) {
|
|
56
|
+
flex-direction: row;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
El mixin genera dos reglas excluyentes: desktop desde 1280px bajo `body:not([data-rds-layout="b"])`
|
|
62
|
+
y desde 1024px bajo `body[data-rds-layout="b"]`. `:where()` no aumenta la especificidad.
|
|
63
|
+
Sin atributo, o con `data-rds-layout="a"`, se aplica A. El mismo CSS compilado sirve para ambos layouts.
|
|
64
|
+
Los estilos base quedan fuera del mixin y se comparten.
|
|
65
|
+
|
|
66
|
+
También puede usarse en el nivel raíz, escribiendo los selectores dentro del bloque. Ambas formas aplican los mismos cortes:
|
|
67
|
+
|
|
68
|
+
```scss
|
|
69
|
+
@include rds.breakpoint(desktop) {
|
|
70
|
+
.card {
|
|
71
|
+
flex-direction: row;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
En el nivel raíz, Sass no permite al mixin inspeccionar los selectores del bloque, así que el contexto se añade delante (`:where(body…) .card`). Por eso, dentro del bloque solo deben ir selectores cuyos ancestros estén dentro del `body`. Usa la forma anidada cuando el selector:
|
|
77
|
+
|
|
78
|
+
- sea `:root`, `html` o `body`;
|
|
79
|
+
- dependa de una clase o atributo del `body` o del `html`, como `.dark .card`, `[dir='rtl'] .card` o `.no-js .card`.
|
|
80
|
+
|
|
81
|
+
Tampoco escribas declaraciones sueltas en el nivel raíz: se aplicarían sobre el propio `body`.
|
|
82
|
+
|
|
83
|
+
Los breakpoints anidados reutilizan el layout del breakpoint exterior, así que solo se añade un contexto:
|
|
84
|
+
|
|
85
|
+
```scss
|
|
86
|
+
.card {
|
|
87
|
+
@include rds.breakpoint(tablet) {
|
|
88
|
+
gap: 16px;
|
|
89
|
+
|
|
90
|
+
@include rds.breakpoint(desktop) {
|
|
91
|
+
gap: 24px; // A: 720px y 1280px · B: 800px y 1024px
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
En la forma anidada, la condición del layout se comprueba sobre el propio elemento seleccionado, así que funcionan también las condiciones del `body` o del `html` y los propios `body`, `html` y `:root`:
|
|
98
|
+
|
|
99
|
+
```scss
|
|
100
|
+
:root {
|
|
101
|
+
--page-gap: 16px;
|
|
102
|
+
|
|
103
|
+
@include rds.breakpoint(desktop) {
|
|
104
|
+
--page-gap: 24px;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.dark .card {
|
|
109
|
+
@include rds.breakpoint(tablet) {
|
|
110
|
+
padding: 24px; // funciona aunque .dark esté en el body
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- La condición se inserta al principio del elemento seleccionado: `.dark :where(body[data-rds-layout='b'], body[data-rds-layout='b'] *).card`. Así siguen funcionando `&--modificador`, `&__elemento`, `&:hover` y los pseudoelementos.
|
|
116
|
+
- `html` y `:root` comprueban su `body` hijo con `:has()`: `:where(:has(> body[data-rds-layout='b'])):root`. Requiere Chrome 105, Safari 15.4 o Firefox 121; en navegadores sin `:has()` esa regla no se aplica y se mantiene el valor base.
|
|
117
|
+
- Todas las condiciones van dentro de `:where()`, así que la especificidad no cambia.
|
|
118
|
+
- Un selector que ya limita el layout a mano (`body[data-rds-layout='b'] .card`) compila y solo se aplica en Layout B, con los cortes de B.
|
|
119
|
+
|
|
120
|
+
Las claves personalizadas añadidas al mapa `$breakpoints` son cortes fijos que no dependen del `body`.
|
|
121
|
+
Los valores se leen del mapa `$breakpoints` configurado por el consumidor. Si falta uno de los dos cortes de un nombre semántico, la compilación falla.
|
|
122
|
+
Una clave desconocida mantiene el warning histórico.
|
|
123
|
+
|
|
124
|
+
El contexto se resuelve a partir del `body` del documento, así que no funciona dentro de Shadow DOM. Un iframe utiliza el atributo de su propio `body`.
|
|
125
|
+
En Vue `scoped`, el contexto global debe quedar fuera del scope: utiliza un bloque de estilos no scoped para estas reglas.
|
|
126
|
+
El atributo global del `body` selecciona B y las clases `.rds-layout-max` y `.rds-grid` adaptan sus reglas al contexto.
|
|
127
|
+
|
|
128
|
+
Si distribuyes CSS precompilado, el atributo del `body` debe estar presente en la página para seleccionar el perfil correspondiente.
|
|
129
|
+
|
|
130
|
+
Las primitivas internas que necesitan cortes fijos (helpers `rds-col-*`, Flex responsive y la escala tipográfica) usan un mixin interno que no forma parte de la API pública.
|
|
131
|
+
En páginas sin atributo, los cortes son los mismos que antes: no es necesario migrar las integraciones existentes de Layout A.
|
|
132
|
+
|
|
133
|
+
### Contenedores
|
|
134
|
+
|
|
135
|
+
LEGACY
|
|
136
|
+
`.rds-e-layout-max`
|
|
137
|
+
Alias compatible de `.rds-layout-max`, con el mismo comportamiento contextual.
|
|
138
|
+
CONTEXTUAL A/B
|
|
139
|
+
`.rds-layout-max`
|
|
140
|
+
Layout A: padding 16px mobile, 24px tablet y 32px desktop, máximo 1600px. Layout B: padding progresivo hasta 72px, máximo 1584px.
|
|
141
|
+
MIXIN SASS
|
|
142
|
+
`@include layout-max`
|
|
143
|
+
El mismo contenedor aplicado a tu propio selector, para cuando no puedes o no quieres añadir la clase al HTML.
|
|
144
|
+
|
|
145
|
+
`.rds-layout-max` sigue el layout declarado en `body[data-rds-layout]`: aplica los márgenes de A sin atributo (o con `"a"`) y los de B con `data-rds-layout="b"`. Así, componentes y plantillas transversales no necesitan conocer el layout de la página.
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<body data-rds-layout="b">
|
|
149
|
+
<main class="rds-layout-max">...</main>
|
|
150
|
+
</body>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- No existe un contenedor editorial local: el contenedor siempre sigue al `body`.
|
|
154
|
+
- Los selectores de contexto usan `:where()`, así que la especificidad es la misma que la de una clase simple.
|
|
155
|
+
|
|
156
|
+
#### Como mixin Sass
|
|
157
|
+
|
|
158
|
+
Si no puedes añadir la clase al HTML (por ejemplo, en un componente de AEM o en una plantilla de terceros), aplica el mismo contenedor desde Sass con `layout-max`:
|
|
159
|
+
|
|
160
|
+
```scss
|
|
161
|
+
@use 'energy-foundations/src/rds-variables' as rds;
|
|
162
|
+
|
|
163
|
+
.portal-main {
|
|
164
|
+
@include rds.layout-max;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Genera exactamente las mismas reglas que `.rds-layout-max`, aplicadas a tu selector: los márgenes de A o de B según el atributo del `body`, sin parámetros que elegir.
|
|
169
|
+
|
|
170
|
+
- Debe usarse dentro de un selector; en el nivel raíz la compilación falla con un mensaje claro.
|
|
171
|
+
- Funciona con cualquier selector, también sobre el propio `body` o con condiciones del `body` o del `html`, como `.dark .portal-main`.
|
|
172
|
+
- La especificidad es la de tu selector: el mixin no la aumenta.
|
|
173
|
+
- Puedes combinarlo con tus propias reglas; declara después las que quieras que prevalezcan.
|
|
174
|
+
|
|
175
|
+
### Composición contextual
|
|
176
|
+
|
|
177
|
+
El contenedor `.rds-layout-max` y el grid `.rds-grid` siguen al `body`, por lo que la página no necesita seleccionar una clase de grid editorial alternativa.
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<body data-rds-layout="b">
|
|
181
|
+
<main class="rds-layout-max">
|
|
182
|
+
<div class="rds-grid"><!-- Contenido --></div>
|
|
183
|
+
</main>
|
|
184
|
+
</body>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Con `data-rds-layout="b"`, la tipografía, el contenedor y el grid usan los cortes de Layout B. Para Layout A, omite el atributo. El contenedor solo controla ancho y márgenes; el grid controla columnas y gaps.
|
|
188
|
+
|
|
189
|
+
Resuelve el perfil una sola vez en el shell de Vue o en el Page Component de AEM. Al cambiar de ruta, actualiza el contexto global del destino y las primitivas se adaptan.
|
|
190
|
+
|
|
191
|
+
Esta composición no cambia los helpers `rds-col-*` ni Flex responsive: siguen usando los breakpoints de A. Para spans editoriales, utiliza las reglas Sass del perfil B descritas en Grid.
|
|
192
|
+
|
|
193
|
+
### Referencia de Grid
|
|
194
|
+
|
|
195
|
+
Columnas, gaps, offsets y ejemplos interactivos tienen su propia página en Layout / Grid.
|
|
196
|
+
|
|
197
|
+
### Tipografía responsive
|
|
198
|
+
|
|
199
|
+
Las clases y el mixin de tipografía leen el contexto global. No es necesario elegir un mixin distinto por layout:
|
|
200
|
+
|
|
201
|
+
```scss
|
|
202
|
+
.component__title {
|
|
203
|
+
@include rds-font-style('headline-01');
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<h1 class="rds-headline-01">Título responsive</h1>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Mobile Base Ambos layouts
|
|
212
|
+
Desktop A · 1280px B · 1024px
|
|
213
|
+
Ultrawide A · 1600px B · 1584px
|
|
214
|
+
|
|
215
|
+
No existe una escala tipográfica específica para tablet. El breakpoint `desktop-large` modifica la maquetación, pero no introduce por sí mismo una nueva escala tipográfica.
|
|
216
|
+
|
|
217
|
+
## Grid: columnas, offsets y perfil editorial
|
|
218
|
+
|
|
219
|
+
### Columnas y offsets · Layout A
|
|
220
|
+
|
|
221
|
+
El perfil A usa 4 columnas con gap de 16px hasta 719px, 8 columnas con gap de 16px desde 720px y 12 columnas con gap de 24px desde 1280px.
|
|
222
|
+
El perfil B usa 4 columnas con gap de 16px hasta 799px, 8 columnas con gap de 24px desde 800px y 12 columnas con gap de 24px desde 1024px.
|
|
223
|
+
|
|
224
|
+
El patrón `rds-col-{abbr}-{n}` selecciona el span; `rds-col-{abbr}-{n}-offset-{offset}` fija el inicio en offset + 1.
|
|
225
|
+
El helper `rds-col-{n}` solo se genera en los rangos que admiten ese número de columnas: no reduce un span grande para que quepa en mobile.
|
|
226
|
+
|
|
227
|
+
No se generan aliases `sm`, `lg`, `xl` ni columnas `xd`. Las reglas `md` permanecen activas en desktop si no las sustituyes.
|
|
228
|
+
Los offsets disponibles van de cero a `máximo de columnas - n`.
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<div class="rds-layout-max">
|
|
232
|
+
<div class="rds-grid">
|
|
233
|
+
<article class="rds-col-sd-4 rds-col-md-4 rds-col-ld-6">
|
|
234
|
+
Contenido
|
|
235
|
+
</article>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```html
|
|
241
|
+
<article class="rds-col-sd-4 rds-col-md-4-offset-2">
|
|
242
|
+
En tablet ocupa 4 columnas y empieza en la tercera.
|
|
243
|
+
</article>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Grid contextual · Layout B
|
|
247
|
+
|
|
248
|
+
El grid `.rds-grid` usa automáticamente los cortes de B cuando el `body` tiene `data-rds-layout="b"`.
|
|
249
|
+
Los helpers `rds-col-*` actuales se generan con los cortes de A. Para spans que sigan el layout de la página, define las columnas en el Sass del componente con `breakpoint`, que aplica los cortes de A o B según el `body`:
|
|
250
|
+
|
|
251
|
+
```html
|
|
252
|
+
<div class="rds-layout-max">
|
|
253
|
+
<div class="rds-grid">
|
|
254
|
+
<article class="portal-editorial-card">Contenido</article>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
```scss
|
|
260
|
+
@use 'energy-foundations/src/rds-variables' as rds;
|
|
261
|
+
|
|
262
|
+
.portal-editorial-card {
|
|
263
|
+
grid-column: span 4;
|
|
264
|
+
|
|
265
|
+
@include rds.breakpoint(tablet) {
|
|
266
|
+
grid-column: span 4; // A: desde 720px · B: desde 800px
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@include rds.breakpoint(desktop) {
|
|
270
|
+
grid-column: span 6; // A: desde 1280px · B: desde 1024px
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Prueba el grid
|
|
276
|
+
|
|
277
|
+
## Integración Vue: público B y privado A
|
|
278
|
+
|
|
279
|
+
### Caso habitual: público B, privado A
|
|
280
|
+
|
|
281
|
+
Una misma SPA puede utilizar Layout B en la parte pública y Layout A después del login. Se comparte el mismo bundle CSS y no es necesario que los componentes conozcan el layout.
|
|
282
|
+
|
|
283
|
+
#### 1. Declara el layout en Vue Router
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
287
|
+
|
|
288
|
+
type RdsLayout = 'a' | 'b'
|
|
289
|
+
|
|
290
|
+
declare module 'vue-router' {
|
|
291
|
+
interface RouteMeta {
|
|
292
|
+
rdsLayout?: RdsLayout
|
|
293
|
+
requiresAuth?: boolean
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export const routes: RouteRecordRaw[] = [
|
|
298
|
+
{
|
|
299
|
+
path: '/',
|
|
300
|
+
component: () => import('./layouts/PublicLayout.vue'),
|
|
301
|
+
meta: { rdsLayout: 'b' },
|
|
302
|
+
children: [
|
|
303
|
+
{ path: '', component: () => import('./views/HomeView.vue') },
|
|
304
|
+
{ path: 'login', component: () => import('./views/LoginView.vue') },
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
path: '/app',
|
|
309
|
+
component: () => import('./layouts/PrivateLayout.vue'),
|
|
310
|
+
meta: { rdsLayout: 'a', requiresAuth: true },
|
|
311
|
+
children: [
|
|
312
|
+
{ path: '', component: () => import('./views/DashboardView.vue') },
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
]
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Las rutas hijas heredan la metadata de la ruta padre y pueden sobrescribirla cuando exista una excepción.
|
|
319
|
+
|
|
320
|
+
#### 2. Sincroniza el `body` desde un único punto
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import type { Router } from 'vue-router'
|
|
324
|
+
|
|
325
|
+
type RdsLayout = 'a' | 'b'
|
|
326
|
+
|
|
327
|
+
function applyRdsLayout(layout: RdsLayout): void {
|
|
328
|
+
if (layout === 'b') {
|
|
329
|
+
document.body.dataset.rdsLayout = 'b'
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
document.body.removeAttribute('data-rds-layout')
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function installRdsLayoutSync(router: Router): void {
|
|
337
|
+
router.afterEach((to) => {
|
|
338
|
+
applyRdsLayout(to.meta.rdsLayout ?? 'a')
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
const router = createRouter({
|
|
345
|
+
history: createWebHistory(),
|
|
346
|
+
routes,
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
installRdsLayoutSync(router)
|
|
350
|
+
|
|
351
|
+
await router.isReady()
|
|
352
|
+
|
|
353
|
+
createApp(App)
|
|
354
|
+
.use(router)
|
|
355
|
+
.mount('#app')
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### 3. Mantén autenticación y presentación separadas
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
router.beforeEach((to) => {
|
|
362
|
+
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
|
|
363
|
+
return {
|
|
364
|
+
path: '/login',
|
|
365
|
+
query: { redirect: to.fullPath },
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
El guard decide si la navegación está permitida. `afterEach` aplica el layout de la ruta finalmente confirmada. No obtengas el layout directamente de `isAuthenticated`: podría existir una página pública A o una sección autenticada B.
|
|
372
|
+
|
|
373
|
+
### Casos especiales
|
|
374
|
+
|
|
375
|
+
## Integración Adobe AEM y Catálogo Zero
|
|
376
|
+
|
|
377
|
+
### Un único propietario: el Page Component
|
|
378
|
+
|
|
379
|
+
El layout debe resolverse en el Page Component, template o policy porque esa capa controla el `body`.
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
<!-- Layout A: valor por defecto -->
|
|
383
|
+
<body>
|
|
384
|
+
<main class="rds-layout-max">
|
|
385
|
+
<div class="rds-grid"><!-- Contenido --></div>
|
|
386
|
+
</main>
|
|
387
|
+
</body>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```html
|
|
391
|
+
<!-- Layout B: selección editorial -->
|
|
392
|
+
<body data-rds-layout="b">
|
|
393
|
+
<main class="rds-layout-max">
|
|
394
|
+
<div class="rds-grid"><!-- Contenido --></div>
|
|
395
|
+
</main>
|
|
396
|
+
</body>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
La selección puede proceder del template, una Page Policy, una propiedad de página o la configuración global del portal.
|
|
400
|
+
|
|
401
|
+
### Contrato Sling Model → HTL
|
|
402
|
+
|
|
403
|
+
El Sling Model debe normalizar la configuración y exponer valores seguros y predecibles:
|
|
404
|
+
|
|
405
|
+
```html
|
|
406
|
+
<body data-rds-layout="${pageModel.rdsLayout}">
|
|
407
|
+
<main class="${pageModel.layoutContainerClass}">
|
|
408
|
+
<div class="${pageModel.gridClass}">
|
|
409
|
+
<!-- Contenido de la página -->
|
|
410
|
+
</div>
|
|
411
|
+
</main>
|
|
412
|
+
</body>
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
El modelo debe aceptar únicamente una whitelist de valores conocidos. No copies texto libre del autor directamente en atributos o clases. Para Layout A, HTL debe omitir el atributo vacío o dejarlo sin efecto.
|
|
416
|
+
|
|
417
|
+
### Catálogo Zero
|
|
418
|
+
|
|
419
|
+
Los componentes comunes y moleculares:
|
|
420
|
+
|
|
421
|
+
- No consultan la propiedad de página.
|
|
422
|
+
- No reciben un parámetro `layout` para elegir tipografía.
|
|
423
|
+
- No se duplican como componente A y componente B.
|
|
424
|
+
- Permiten que el Page Component y Foundations resuelvan el contexto.
|
|
425
|
+
|
|
426
|
+
Los componentes específicos de portal siguen el mismo contrato. `.rds-layout-max` adapta sus márgenes al atributo del `body`, pero ninguna clase cambia por sí sola los breakpoints tipográficos de la página.
|
|
427
|
+
|
|
428
|
+
Si la plantilla HTL no permite añadir la clase al contenedor, aplica el mismo comportamiento desde el Sass del componente:
|
|
429
|
+
|
|
430
|
+
```scss
|
|
431
|
+
@use 'energy-foundations/src/rds-variables' as rds;
|
|
432
|
+
|
|
433
|
+
.cmp-page__main {
|
|
434
|
+
@include rds.layout-max;
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
package/llms/index.md
CHANGED
|
@@ -5,6 +5,9 @@ Generated for intelligent coding assistants.
|
|
|
5
5
|
## Guides
|
|
6
6
|
|
|
7
7
|
- [Installation & Quick Start](./00-installation.md)
|
|
8
|
+
- [Layouts A/B, Grid and Vue/AEM integration](./04-layouts.md)
|
|
9
|
+
|
|
10
|
+
For responsive pages, read the layout guide first: body context controls typography, while containers and grids are explicit choices. Layout A remains the default; B is opt-in. Column and responsive Flex helpers still use A breakpoints.
|
|
8
11
|
|
|
9
12
|
## Classes
|
|
10
13
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "energy-foundations",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0-beta.1",
|
|
4
4
|
"config": {
|
|
5
5
|
"commitizen": {
|
|
6
6
|
"path": "node_modules/cz-customizable"
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
],
|
|
65
65
|
"lint-staged": {
|
|
66
66
|
"*.{vue,ts}": [
|
|
67
|
-
"
|
|
67
|
+
"eslint"
|
|
68
68
|
],
|
|
69
69
|
"*.scss": [
|
|
70
|
-
"
|
|
70
|
+
"stylelint"
|
|
71
71
|
],
|
|
72
72
|
"package.json": [
|
|
73
73
|
"fixpack --dryRun"
|
|
@@ -97,7 +97,9 @@
|
|
|
97
97
|
"storybook": "storybook dev -p 6006",
|
|
98
98
|
"storybook:preview": "npx http-server storybook-static",
|
|
99
99
|
"stylelint": "npx stylelint '**/*.scss'",
|
|
100
|
-
"stylelint:fix": "npx stylelint '**/*.scss' --fix"
|
|
100
|
+
"stylelint:fix": "npx stylelint '**/*.scss' --fix",
|
|
101
|
+
"test:breakpoints": "node tests/verify-breakpoints.mjs",
|
|
102
|
+
"test:typography": "node tests/verify-typography.mjs"
|
|
101
103
|
},
|
|
102
104
|
"style": "dist/style.css",
|
|
103
105
|
"type": "module"
|
|
@@ -3,46 +3,13 @@
|
|
|
3
3
|
*************************/
|
|
4
4
|
@use 'sass:map';
|
|
5
5
|
@use '../rds-variables' as *;
|
|
6
|
+
@use '../mixins/breakpoint-fixed' as *;
|
|
6
7
|
|
|
8
|
+
// Contextual container: follows the page layout declared on body[data-rds-layout].
|
|
9
|
+
// Also available as the `layout-max` mixin for custom selectors.
|
|
7
10
|
.rds-e-layout-max,
|
|
8
11
|
.rds-layout-max {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@include breakpoint(tablet) {
|
|
12
|
-
padding: 0 $g-padding-tablet;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@include breakpoint(desktop) {
|
|
16
|
-
padding: 0 $g-padding-desktop;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@include breakpoint(ultrawide) {
|
|
20
|
-
margin: 0 auto;
|
|
21
|
-
max-width: 1600px;
|
|
22
|
-
padding: 0 $g-padding-ultrawide;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.rds-layout-editorial {
|
|
27
|
-
padding: 0 $g-padding-mobile;
|
|
28
|
-
|
|
29
|
-
@include breakpoint(tablet-v2) {
|
|
30
|
-
padding: 0 $g-padding-tablet;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@include breakpoint(desktop-v2) {
|
|
34
|
-
padding: 0 $g-padding-desktop;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@include breakpoint(desktop-large-v2) {
|
|
38
|
-
padding: 0 72px;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@include breakpoint(ultrawide-v2) {
|
|
42
|
-
margin: 0 auto;
|
|
43
|
-
max-width: 1584px;
|
|
44
|
-
padding: 0 72px;
|
|
45
|
-
}
|
|
12
|
+
@include layout-max;
|
|
46
13
|
}
|
|
47
14
|
|
|
48
15
|
.rds-grid {
|
|
@@ -52,23 +19,20 @@
|
|
|
52
19
|
width: 100%;
|
|
53
20
|
|
|
54
21
|
@each $breakpoint, $config in $grid-config {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
22
|
+
:where(body:not([data-rds-layout='b'])) & {
|
|
23
|
+
@include fixed-breakpoint($breakpoint) {
|
|
24
|
+
gap: map.get($config, gap);
|
|
25
|
+
grid-template-columns: repeat(map.get($config, max-columns), 1fr);
|
|
26
|
+
}
|
|
58
27
|
}
|
|
59
28
|
}
|
|
60
|
-
}
|
|
61
29
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@each $breakpoint, $config in $grid-config-editorial {
|
|
69
|
-
@include breakpoint($breakpoint) {
|
|
70
|
-
gap: map.get($config, gap);
|
|
71
|
-
grid-template-columns: repeat(map.get($config, max-columns), 1fr);
|
|
30
|
+
@each $breakpoint, $config in $grid-config-b {
|
|
31
|
+
:where(body[data-rds-layout='b']) & {
|
|
32
|
+
@include fixed-breakpoint($breakpoint) {
|
|
33
|
+
gap: map.get($config, gap);
|
|
34
|
+
grid-template-columns: repeat(map.get($config, max-columns), 1fr);
|
|
35
|
+
}
|
|
72
36
|
}
|
|
73
37
|
}
|
|
74
38
|
}
|
|
@@ -86,7 +50,7 @@ $breakpoint-abbr: (
|
|
|
86
50
|
$max-columns: map.get($config, max-columns);
|
|
87
51
|
$abbr: map.get($breakpoint-abbr, $breakpoint);
|
|
88
52
|
|
|
89
|
-
@include breakpoint($breakpoint) {
|
|
53
|
+
@include fixed-breakpoint($breakpoint) {
|
|
90
54
|
// Clases sin offset: rds-col-sd-3
|
|
91
55
|
@for $i from 1 through $max-columns {
|
|
92
56
|
.rds-col-#{$abbr}-#{$i} {
|
|
@@ -111,7 +75,7 @@ $breakpoint-abbr: (
|
|
|
111
75
|
|
|
112
76
|
@for $i from 1 through $max-columns {
|
|
113
77
|
.rds-col-#{$i} {
|
|
114
|
-
@include breakpoint($breakpoint) {
|
|
78
|
+
@include fixed-breakpoint($breakpoint) {
|
|
115
79
|
grid-column: span #{$i};
|
|
116
80
|
}
|
|
117
81
|
}
|
|
@@ -153,7 +117,7 @@ $flex-prefix: 'rds-flex';
|
|
|
153
117
|
@each $breakpoint, $config in $grid-config {
|
|
154
118
|
$abbr: map.get($breakpoint-abbr, $breakpoint);
|
|
155
119
|
|
|
156
|
-
@include breakpoint($breakpoint) {
|
|
120
|
+
@include fixed-breakpoint($breakpoint) {
|
|
157
121
|
.#{$flex-prefix}-#{$abbr}-row {
|
|
158
122
|
flex-direction: row;
|
|
159
123
|
}
|
|
@@ -186,7 +150,7 @@ $flex-prefix: 'rds-flex';
|
|
|
186
150
|
@each $breakpoint, $config in $grid-config {
|
|
187
151
|
$abbr: map.get($breakpoint-abbr, $breakpoint);
|
|
188
152
|
|
|
189
|
-
@include breakpoint($breakpoint) {
|
|
153
|
+
@include fixed-breakpoint($breakpoint) {
|
|
190
154
|
.#{$flex-prefix}-grow-#{$abbr} {
|
|
191
155
|
flex-grow: 1;
|
|
192
156
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@use 'sass:map';
|
|
2
|
+
@use '../variables/layout' as *;
|
|
3
|
+
|
|
4
|
+
// Internal fixed media query, independent of body[data-rds-layout].
|
|
5
|
+
// Not forwarded by rds-variables: consumers must use `breakpoint`.
|
|
6
|
+
@mixin fixed-breakpoint($breakpoint) {
|
|
7
|
+
$value: map.get($breakpoints, $breakpoint);
|
|
8
|
+
|
|
9
|
+
// If the key exists in the map
|
|
10
|
+
@if $value {
|
|
11
|
+
@if $breakpoint == mobile or $breakpoint == mobile-v2 {
|
|
12
|
+
@media (max-width: $value) {
|
|
13
|
+
@content;
|
|
14
|
+
}
|
|
15
|
+
} @else {
|
|
16
|
+
@media (min-width: $value) {
|
|
17
|
+
@content;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// If the key doesn't exist in the map
|
|
23
|
+
@else {
|
|
24
|
+
@warn "Unfortunately, no value could be retrieved from `#{$breakpoint}`. " + "Please make sure it is defined in `$breakpoints` map.";
|
|
25
|
+
}
|
|
26
|
+
}
|