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.
@@ -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.4.0",
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
- "yarn lint"
67
+ "eslint"
68
68
  ],
69
69
  "*.scss": [
70
- "yarn stylelint"
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
- padding: 0 $g-padding-mobile;
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
- @include breakpoint($breakpoint) {
56
- gap: map.get($config, gap);
57
- grid-template-columns: repeat(map.get($config, max-columns), 1fr);
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
- .rds-grid-editorial {
63
- display: grid;
64
- margin-left: auto;
65
- margin-right: auto;
66
- width: 100%;
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
  }
@@ -1,4 +1,4 @@
1
- /** Auto-generated by generateIconLayers.js on 2026-09-16T05:39:24.510Z */
1
+ /** Auto-generated by generateIconLayers.js on 2026-10-19T12:32:44.265Z */
2
2
  /* eslint-disable */
3
3
  export const iconLayers = {
4
4
  "calendar_active": 8,
@@ -1,4 +1,4 @@
1
- /** Auto-generated by generateIconLayers.js on 2026-09-16T05:39:24.510Z */
1
+ /** Auto-generated by generateIconLayers.js on 2026-10-19T12:32:44.265Z */
2
2
  /* eslint-disable */
3
3
  export const iconLayers: Record<string, number> = {
4
4
  "calendar_active": 8,
@@ -1,4 +1,4 @@
1
- /** Auto-generated by generateIconLayers.js on 2026-09-16T05:39:24.510Z */
1
+ /** Auto-generated by generateIconLayers.js on 2026-10-19T12:32:44.265Z */
2
2
  /* eslint-disable */
3
3
  export const iconNames = [
4
4
  "ac-aire-acondicionado",
@@ -1,4 +1,4 @@
1
- /** Auto-generated by generateIconLayers.js on 2026-09-16T05:39:24.510Z */
1
+ /** Auto-generated by generateIconLayers.js on 2026-10-19T12:32:44.265Z */
2
2
  /* eslint-disable */
3
3
  export const iconNames: string[] = [
4
4
  "ac-aire-acondicionado",
@@ -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
+ }