@edc-motor/ui 0.4.25 → 0.4.26

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edc-motor/ui",
3
- "version": "0.4.25",
3
+ "version": "0.4.26",
4
4
  "description": "EdC Motor — componentes públicos Vue 3 + tokens SCSS para webs de juegos de mesa (paquete fuente: lo compila el consumidor con Vite)",
5
5
  "license": "GPL-3.0-only",
6
6
  "type": "module",
package/scss/_tokens.scss CHANGED
@@ -38,7 +38,7 @@ $k-fw-bold: 700;
38
38
  $k-lh-tight: 1.15;
39
39
  $k-lh-ui: 1.35;
40
40
  $k-lh-body: 1.55;
41
- $fs-11: 11px; $fs-12: 12px; $fs-13: 13px; $fs-14: 14px; $fs-16: 16px; $fs-18: 18px; $fs-20: 20px; $fs-24: 24px; $fs-28: 28px; $fs-32: 32px;
41
+ $fs-11: 11px; $fs-12: 12px; $fs-13: 13px; $fs-14: 14px; $fs-16: 16px; $fs-18: 18px; $fs-20: 20px; $fs-24: 24px; $fs-28: 28px; $fs-32: 32px; $fs-36: 36px; $fs-40: 40px;
42
42
 
43
43
  // Espaciado (px, como kontuan)
44
44
  $space-0: 0;
@@ -18,14 +18,19 @@
18
18
  &--w-narrow .block__inner { max-width: 800px; }
19
19
  &--w-full .block__inner { max-width: none; padding-inline: $space-6; }
20
20
 
21
- // Tipografía de bloque: texto $fs-16, subtítulo $fs-20, título $fs-28
22
- // (la cabecera sube a 32/24). Los títulos nunca se justifican.
21
+ // Tipografía de bloque, escalada ~×1.125 sobre la original para que el
22
+ // texto BASE sea de 18px: texto $fs-18, subtítulo 22, título $fs-32 (la
23
+ // cabecera sube a 36/28). Los títulos nunca se justifican.
23
24
  // width 100%: en grids con justify-items start (el cuerpo del CTA) el
24
25
  // elemento encoge a su contenido y el text-align no se vería.
25
- &__title { margin: 0; font-size: $fs-28; width: 100%; }
26
+ &__title { margin: 0; font-size: $fs-32; width: 100%; }
26
27
  // pre-line: el subtítulo es un textarea — sus saltos de línea cuentan.
27
- &__subtitle { margin: 0; color: $text-2; font-size: $fs-20; white-space: pre-line; width: 100%; }
28
- &__text { color: $text-1; font-size: $fs-16; }
28
+ &__subtitle { margin: 0; color: $text-2; font-size: 22px; white-space: pre-line; width: 100%; }
29
+ &__text { color: $text-1; font-size: $fs-18; }
30
+
31
+ // El wysiwyg fija su propio cuerpo ($fs-16): dentro de un bloque manda la
32
+ // base de bloque (18px). La cita lo re-sube luego a su tamaño.
33
+ .rich-content { font-size: $fs-18; }
29
34
 
30
35
  &--align-justify &__title,
31
36
  &--align-justify &__subtitle { text-align: left; }
@@ -52,17 +57,20 @@
52
57
  &--subtitle-right &__subtitle { text-align: right; }
53
58
  }
54
59
 
60
+ // Etiqueta de la tarjeta de texto: SIN chip — solo texto coloreado de
61
+ // acento, con alineación propia (campo label_align del bloque).
55
62
  &__label {
56
- display: inline-block;
57
- justify-self: start;
58
- font-size: $fs-12;
63
+ display: block;
64
+ width: 100%;
65
+ font-size: $fs-13;
59
66
  font-weight: $k-fw-semibold;
60
67
  text-transform: uppercase;
61
68
  letter-spacing: 0.05em;
62
69
  color: $accent-500;
63
- border: 1px solid color-mix(in srgb, $accent-500 45%, transparent);
64
- border-radius: $radius-pill;
65
- padding: 2px $space-3;
70
+
71
+ &--left { text-align: left; }
72
+ &--center { text-align: center; }
73
+ &--right { text-align: right; }
66
74
  }
67
75
 
68
76
  &__card {
@@ -80,8 +88,8 @@
80
88
  .edc-button { justify-self: start; text-decoration: none; }
81
89
  }
82
90
 
83
- &--header .block__title { font-size: $fs-32; }
84
- &--header .block__subtitle { font-size: $fs-24; }
91
+ &--header .block__title { font-size: $fs-36; }
92
+ &--header .block__subtitle { font-size: $fs-28; }
85
93
 
86
94
  &__media-layout { display: grid; gap: $space-4; }
87
95
  &__image {
@@ -90,9 +98,11 @@
90
98
  justify-self: center;
91
99
  }
92
100
 
101
+ // Columnas solo de $bp-md (768) para arriba: por debajo el bloque pasa a
102
+ // VERTICAL y la imagen va SIEMPRE encima del texto (regla más abajo).
93
103
  &--image-left .block__media-layout,
94
104
  &--image-right .block__media-layout {
95
- @container content (min-width: #{$bp-sm}) {
105
+ @container content (min-width: #{$bp-md}) {
96
106
  // Reparto izquierda:derecha del campo image_columns (--img-cols).
97
107
  grid-template-columns: var(--img-cols, minmax(0, 2fr) minmax(0, 3fr));
98
108
  align-items: stretch;
@@ -102,6 +112,16 @@
102
112
  &--image-right .block__media-layout .block__image { order: 2; }
103
113
  &--image-bottom .block__media-layout .block__image { order: 2; }
104
114
 
115
+ // En vertical (contenedor < 768) la imagen SIEMPRE encima del texto, da
116
+ // igual su posición configurada (derecha/abajo incluidas). Después de las
117
+ // reglas de order de arriba para ganarles el empate.
118
+ @container content (max-width: #{$bp-md}) {
119
+ &[class*="block--image-"] .block__media-layout > .block__image,
120
+ &[class*="block--image-"] .block__media-layout > .block__image-frame {
121
+ order: -1;
122
+ }
123
+ }
124
+
105
125
  // Marco de la imagen en columnas: el ALTO lo marca el texto de al lado
106
126
  // (la imagen absoluta no aporta altura) y el modo de escalado hace el resto.
107
127
  &__image-frame {
@@ -117,12 +137,14 @@
117
137
  }
118
138
  }
119
139
 
120
- &--fit-contain &__image-frame .block__image { object-fit: contain; }
121
- &--fit-cover &__image-frame .block__image { object-fit: cover; }
140
+ // object-position top: con el marco estirado a la altura del texto,
141
+ // "contain" centraba la imagen en vertical — SIEMPRE anclada arriba.
142
+ &--fit-contain &__image-frame .block__image { object-fit: contain; object-position: top; }
143
+ &--fit-cover &__image-frame .block__image { object-fit: cover; object-position: top; }
122
144
  &--fit-fill &__image-frame .block__image { object-fit: fill; }
123
145
 
124
- // En una columna (estrecho) la imagen vuelve al flujo normal.
125
- @container content (max-width: #{$bp-sm}) {
146
+ // En una columna (vertical, < 768) la imagen vuelve al flujo normal.
147
+ @container content (max-width: #{$bp-md}) {
126
148
  &__image-frame {
127
149
  position: static;
128
150
  min-height: 0;
@@ -137,7 +159,9 @@
137
159
  }
138
160
 
139
161
  // Imagen flotada: el texto la rodea y sigue por debajo (cleartext, como
140
- // CDL). En estrecho la imagen va arriba a lo ancho, sin flotar.
162
+ // CDL), con un margen de $space-4 (16px) hacia el texto por los lados que
163
+ // lo tocan. En vertical (< 768) la imagen va arriba a lo ancho, sin
164
+ // flotar.
141
165
  &--image-clear-left .block__media-layout,
142
166
  &--image-clear-right .block__media-layout {
143
167
  display: block;
@@ -145,20 +169,26 @@
145
169
  // Clearfix: el bloque no se corta si la imagen es más alta que el texto.
146
170
  &::after { content: ""; display: table; clear: both; }
147
171
 
148
- .block__image { margin-bottom: $space-3; }
172
+ .block__image { margin-bottom: $space-4; }
149
173
  }
150
174
  &--image-clear-left .block__media-layout .block__image {
151
- @container content (min-width: #{$bp-sm}) {
175
+ @container content (min-width: #{$bp-md}) {
152
176
  float: left;
153
177
  max-width: 50%;
154
178
  margin-right: $space-4;
179
+ // Anclada ARRIBA mientras el texto la rodea (sin hueco previo ni
180
+ // alineación heredada que la descuelgue).
181
+ margin-top: 0;
182
+ vertical-align: top;
155
183
  }
156
184
  }
157
185
  &--image-clear-right .block__media-layout .block__image {
158
- @container content (min-width: #{$bp-sm}) {
186
+ @container content (min-width: #{$bp-md}) {
159
187
  float: right;
160
188
  max-width: 50%;
161
189
  margin-left: $space-4;
190
+ margin-top: 0;
191
+ vertical-align: top;
162
192
  }
163
193
  }
164
194
 
@@ -204,18 +234,20 @@
204
234
  }
205
235
 
206
236
  // La imagen del CTA sangra hasta el borde de la tarjeta según su posición:
207
- // en columnas toca arriba, abajo y su lateral (el título viaja a la
208
- // columna de texto); arriba/abajo toca los laterales. Esquinas siguiendo
209
- // el radio de la tarjeta.
210
- // La tarjeta del CTA lleva un halo SUTIL del acento, igual por los cuatro
211
- // bordes (sin offset), además de su sombra de profundidad normal.
212
- &--cta .block__card {
237
+ // en columnas toca abajo y su lateral (título y subtítulo van SIEMPRE a
238
+ // ancho completo por encima del grid; solo si la tarjeta ABRE con la
239
+ // imagen sangra también por arriba); arriba/abajo toca los laterales.
240
+ // Esquinas siguiendo el radio de la tarjeta.
241
+ // Las tarjetas del CTA y de la tarjeta de texto llevan un halo SUTIL del
242
+ // acento, igual por los cuatro bordes, además de su sombra normal.
243
+ &--cta .block__card,
244
+ &--text-card .block__card {
213
245
  box-shadow: $shadow-sm, 0 0 18px color-mix(in srgb, $accent-500 30%, transparent);
214
246
  }
215
247
 
216
248
  &--cta.block--image-right .block__media-layout .block__image-frame,
217
249
  &--cta.block--image-left .block__media-layout .block__image-frame {
218
- @container content (min-width: #{$bp-sm}) {
250
+ @container content (min-width: #{$bp-md}) {
219
251
  overflow: hidden;
220
252
  min-height: 0;
221
253
 
@@ -223,14 +255,28 @@
223
255
  }
224
256
  }
225
257
  &--cta.block--image-right .block__media-layout .block__image-frame {
226
- @container content (min-width: #{$bp-sm}) {
227
- margin: (-$space-8) (-$space-8) (-$space-8) 0;
228
- border-radius: 0 $radius-lg $radius-lg 0;
258
+ @container content (min-width: #{$bp-md}) {
259
+ margin: 0 (-$space-8) (-$space-8) 0;
260
+ border-radius: 0 0 $radius-lg 0;
229
261
  }
230
262
  }
231
263
  &--cta.block--image-left .block__media-layout .block__image-frame {
232
- @container content (min-width: #{$bp-sm}) {
233
- margin: (-$space-8) 0 (-$space-8) (-$space-8);
264
+ @container content (min-width: #{$bp-md}) {
265
+ margin: 0 0 (-$space-8) (-$space-8);
266
+ border-radius: 0 0 0 $radius-lg;
267
+ }
268
+ }
269
+ // Sin nada por encima (el media-layout abre la tarjeta): sangra también
270
+ // por arriba, con el radio completo de su lateral.
271
+ &--cta.block--image-right .block__card > .block__media-layout:first-child .block__image-frame {
272
+ @container content (min-width: #{$bp-md}) {
273
+ margin-top: -$space-8;
274
+ border-radius: 0 $radius-lg $radius-lg 0;
275
+ }
276
+ }
277
+ &--cta.block--image-left .block__card > .block__media-layout:first-child .block__image-frame {
278
+ @container content (min-width: #{$bp-md}) {
279
+ margin-top: -$space-8;
234
280
  border-radius: $radius-lg 0 0 $radius-lg;
235
281
  }
236
282
  }
@@ -250,11 +296,11 @@
250
296
  border-radius: 0 0 $radius-lg $radius-lg;
251
297
  }
252
298
 
253
- // CTA con imagen en ESTRECHO: da igual la posición elegida — la imagen va
254
- // ARRIBA, a sangre por los laterales de la tarjeta y con proporción 2:1
255
- // (recortada, no deformada). Va DESPUÉS de las reglas por posición para
256
- // ganarles en el empate de especificidad.
257
- @container content (max-width: #{$bp-sm}) {
299
+ // CTA con imagen en VERTICAL (< 768): da igual la posición elegida — la
300
+ // imagen va ARRIBA, a sangre por los laterales de la tarjeta y con
301
+ // proporción 2:1 (recortada, no deformada). Va DESPUÉS de las reglas por
302
+ // posición para ganarles en el empate de especificidad.
303
+ @container content (max-width: #{$bp-md}) {
258
304
  &--cta[class*="block--image-"] .block__card .block__media-layout {
259
305
  > .block__image,
260
306
  > .block__image-frame {
@@ -297,14 +343,14 @@
297
343
  }
298
344
  }
299
345
 
300
- // Cita: sin adorno de borde, en GRANDE ($fs-32, el token más cercano a
301
- // los ~40px pedidos) y del color de acento.
346
+ // Cita: sin adorno de borde, en GRANDE ($fs-36, la escala de bloque
347
+ // subida ~×1.125) y del color de acento.
302
348
  &--quote .block__quote {
303
349
  margin: 0;
304
350
  // Fuente "especial" de la configuración de la web (doc 10); las fuentes
305
351
  // decorativas piden un cuerpo mayor.
306
352
  font-family: var(--font-special, inherit);
307
- font-size: $fs-32;
353
+ font-size: $fs-36;
308
354
  font-style: italic;
309
355
  font-weight: $k-fw-semibold;
310
356
  color: $accent-500;
@@ -319,7 +365,7 @@
319
365
  // El autor no hereda la fuente especial de la cita.
320
366
  font-family: var(--font-body, inherit);
321
367
  font-style: italic;
322
- font-size: $fs-14;
368
+ font-size: $fs-16;
323
369
 
324
370
  &--left { text-align: left; }
325
371
  &--center { text-align: center; }
@@ -337,10 +383,24 @@
337
383
  a { color: $accent-500; text-decoration: none; }
338
384
  a:hover { text-decoration: underline; }
339
385
 
340
- // Tamaño por nivel: 24 22 (sin token entre 20 y 24) 20 (3 o más).
341
- .block__index-level-1 { font-size: $fs-24; }
342
- .block__index-level-2 { font-size: 22px; }
343
- .block__index-level-3 { font-size: $fs-20; }
386
+ // Numerado: la numeración anidada (1, 1.1, 1.2.1…) la pinta el
387
+ // componente fuera los números nativos del ol.
388
+ &--numbered {
389
+ list-style: none;
390
+ padding-left: 0;
391
+ }
392
+
393
+ .block__index-number {
394
+ margin-right: $space-2;
395
+ color: $text-2;
396
+ font-variant-numeric: tabular-nums;
397
+ }
398
+
399
+ // Tamaño por nivel, en la escala de bloque (~×1.125): 27 → 25 → 22
400
+ // (el 3 agrupa "3 o más").
401
+ .block__index-level-1 { font-size: 27px; }
402
+ .block__index-level-2 { font-size: 25px; }
403
+ .block__index-level-3 { font-size: 22px; }
344
404
 
345
405
  // Entradas anidadas: sangría POR NIVEL (profundidad real, sin límite) —
346
406
  // `--depth` lo pone BlockIndex.vue en línea; el tamaño de letra sí se
@@ -361,6 +421,7 @@
361
421
 
362
422
  .block__faq-question {
363
423
  padding: $space-3 $space-4;
424
+ font-size: $fs-18;
364
425
  font-weight: 600;
365
426
  cursor: pointer;
366
427
 
@@ -386,7 +447,7 @@
386
447
  &--large { padding: $space-4 $space-8; }
387
448
  border: 1px solid $accent-500;
388
449
  border-radius: $radius-md;
389
- font-size: $fs-18;
450
+ font-size: $fs-20;
390
451
  font-weight: 600;
391
452
  text-decoration: none;
392
453
  cursor: pointer;
@@ -7,6 +7,12 @@
7
7
  font-size: $fs-16;
8
8
  line-height: $k-lh-body;
9
9
 
10
+ // Las NEGRITAS del wysiwyg, en el color de acento.
11
+ strong,
12
+ b {
13
+ color: $accent-500;
14
+ }
15
+
10
16
  // Aire entre TODOS los elementos del wysiwyg (párrafos, listas, títulos…).
11
17
  p,
12
18
  ul,
@@ -27,22 +27,16 @@ const layoutStyle = computed(() => {
27
27
  ]"
28
28
  >
29
29
  <div class="block__card">
30
- <!-- En columnas, título y subtítulo viajan a la columna de texto para
31
- que la imagen pueda sangrar a TODA la altura de la tarjeta. -->
32
- <template v-if="!inColumns">
33
- <h2 v-if="settings.title" class="block__title">{{ settings.title }}</h2>
34
- <p v-if="settings.subtitle" class="block__subtitle">{{ settings.subtitle }}</p>
35
- </template>
30
+ <!-- Título y subtítulo SIEMPRE a ancho completo, encima del grid: el
31
+ reparto en columnas es solo entre la imagen y el contenido. -->
32
+ <h2 v-if="settings.title" class="block__title">{{ settings.title }}</h2>
33
+ <p v-if="settings.subtitle" class="block__subtitle">{{ settings.subtitle }}</p>
36
34
  <div class="block__media-layout" :style="layoutStyle">
37
35
  <span v-if="settings.image && inColumns" class="block__image-frame">
38
36
  <img class="block__image" :src="String(settings.image)" alt="" />
39
37
  </span>
40
38
  <img v-else-if="settings.image" class="block__image" :src="String(settings.image)" alt="" />
41
39
  <div class="block__cta-body">
42
- <template v-if="inColumns">
43
- <h2 v-if="settings.title" class="block__title">{{ settings.title }}</h2>
44
- <p v-if="settings.subtitle" class="block__subtitle">{{ settings.subtitle }}</p>
45
- </template>
46
40
  <div v-if="settings.body" class="block__text rich-content" v-html="settings.body" />
47
41
  <!-- Alineación propia (button_align; en estrecho el CSS centra
48
42
  siempre) y tamaño grande opcional (button_large) -->
@@ -1,24 +1,43 @@
1
1
  <script setup lang="ts">
2
+ import { computed } from 'vue'
2
3
  import BlockShell from './BlockShell.vue'
3
4
 
4
5
  // Índice automático: enlaces de ancla a los bloques posteriores de la
5
6
  // página, con sangría por nivel (sin límite de profundidad: la escribe
6
7
  // IndexBlock::resolveData subiendo por la cadena de padres del bloque).
7
- defineProps<{
8
+ const props = defineProps<{
8
9
  settings: Record<string, unknown>
9
10
  data: { items?: { id: number; label: string; depth?: number }[] }
10
11
  }>()
12
+
13
+ // Numeración ANIDADA del modo numerado (1, 1.1, 1.2, 1.2.1…): la lista
14
+ // llega plana en preorden con `depth`, así que basta una pila de contadores.
15
+ const numbered = computed(() => {
16
+ const counters: number[] = []
17
+ return (props.data.items ?? []).map((item) => {
18
+ const depth = item.depth ?? 0
19
+ counters.length = depth + 1
20
+ counters[depth] = (counters[depth] ?? 0) + 1
21
+ return { ...item, number: counters.join('.') }
22
+ })
23
+ })
11
24
  </script>
12
25
 
13
26
  <template>
14
27
  <BlockShell :settings="settings" class="block--index">
15
28
  <h2 v-if="settings.title" class="block__title">{{ settings.title }}</h2>
16
29
  <p v-if="settings.subtitle" class="block__subtitle">{{ settings.subtitle }}</p>
17
- <component :is="settings.numbered ? 'ol' : 'ul'" class="block__index">
18
- <!-- Tamaño por nivel (24/22/20, el 3 agrupa "3 o más"); la SANGRÍA, en
19
- cambio, escala con la profundidad real (--depth, sin tope). -->
30
+ <component
31
+ :is="settings.numbered ? 'ol' : 'ul'"
32
+ class="block__index"
33
+ :class="{ 'block__index--numbered': settings.numbered }"
34
+ >
35
+ <!-- Tamaño por nivel (el 3 agrupa "3 o más"); la SANGRÍA, en cambio,
36
+ escala con la profundidad real (--depth, sin tope). Numerado: la
37
+ numeración es ANIDADA (1, 1.1, 1.2.1…), calculada aquí — el ol
38
+ nativo no sabe numerar una lista plana con profundidades. -->
20
39
  <li
21
- v-for="item in data.items ?? []"
40
+ v-for="item in numbered"
22
41
  :key="item.id"
23
42
  :style="{ '--depth': item.depth ?? 0 }"
24
43
  :class="[
@@ -26,6 +45,7 @@ defineProps<{
26
45
  { 'block__index-child': (item.depth ?? 0) > 0 },
27
46
  ]"
28
47
  >
48
+ <span v-if="settings.numbered" class="block__index-number">{{ item.number }}</span>
29
49
  <a :href="`#block-${item.id}`">{{ item.label }}</a>
30
50
  </li>
31
51
  </component>
@@ -12,7 +12,13 @@ defineProps<{ settings: Record<string, unknown>; data?: Record<string, unknown>
12
12
  :class="settings.image ? `block--image-${settings.image_position || 'top'}` : ''"
13
13
  >
14
14
  <div class="block__card">
15
- <span v-if="settings.label" class="block__label">{{ settings.label }}</span>
15
+ <span
16
+ v-if="settings.label"
17
+ class="block__label"
18
+ :class="`block__label--${settings.label_align || 'left'}`"
19
+ >
20
+ {{ settings.label }}
21
+ </span>
16
22
  <h2 v-if="settings.title" class="block__title">{{ settings.title }}</h2>
17
23
  <p v-if="settings.subtitle" class="block__subtitle">{{ settings.subtitle }}</p>
18
24
  <div class="block__media-layout">