lumina-slides 9.0.5 → 9.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/lumina-slides.js +21706 -19324
  2. package/dist/lumina-slides.umd.cjs +223 -223
  3. package/dist/style.css +1 -1
  4. package/package.json +1 -1
  5. package/src/components/LandingPage.vue +1 -1
  6. package/src/components/LuminaDeck.vue +237 -232
  7. package/src/components/base/LuminaElement.vue +2 -0
  8. package/src/components/layouts/LayoutFeatures.vue +123 -123
  9. package/src/components/layouts/LayoutFlex.vue +212 -212
  10. package/src/components/layouts/LayoutStatement.vue +5 -2
  11. package/src/components/layouts/LayoutSteps.vue +108 -108
  12. package/src/components/parts/FlexHtml.vue +65 -65
  13. package/src/components/parts/FlexImage.vue +81 -81
  14. package/src/components/site/SiteDocs.vue +3313 -3314
  15. package/src/components/site/SiteExamples.vue +66 -66
  16. package/src/components/studio/EditorLayoutChart.vue +18 -0
  17. package/src/components/studio/EditorLayoutCustom.vue +18 -0
  18. package/src/components/studio/EditorLayoutVideo.vue +18 -0
  19. package/src/components/studio/LuminaStudioEmbed.vue +68 -0
  20. package/src/components/studio/StudioEmbedRoot.vue +19 -0
  21. package/src/components/studio/StudioInspector.vue +1113 -7
  22. package/src/components/studio/StudioSettings.vue +658 -7
  23. package/src/components/studio/StudioToolbar.vue +20 -2
  24. package/src/composables/useElementState.ts +12 -1
  25. package/src/composables/useFlexLayout.ts +128 -128
  26. package/src/core/Lumina.ts +174 -113
  27. package/src/core/animationConfig.ts +10 -0
  28. package/src/core/elementController.ts +18 -0
  29. package/src/core/elementResolver.ts +4 -2
  30. package/src/core/schema.ts +503 -503
  31. package/src/core/store.ts +465 -465
  32. package/src/core/types.ts +26 -11
  33. package/src/index.ts +2 -2
  34. package/src/utils/templateInterpolation.ts +52 -52
  35. package/src/views/DeckView.vue +313 -313
@@ -1,212 +1,212 @@
1
- <template>
2
- <BaseSlide :data="data" :slide-index="slideIndex">
3
- <div class="flex-layout w-full h-screen flex flex-col overflow-y-auto lg:overflow-hidden"
4
- :style="containerStyle">
5
- <div :class="['flex-1 flex w-full min-h-full lg:h-full', directionClass]"
6
- :style="mainFlexStyle">
7
- <template v-for="(element, i) in data.elements" :key="i">
8
- <!-- Image Element -->
9
- <LuminaElement v-if="element.type === 'image'" :id="elemId(i)" :class="['flex-item', getSizeClass(element.size), element.class]" :style="{ ...getElementStyle(element), ...(element.style || {}) }">
10
- <FlexImage :src="element.src" :alt="element.alt"
11
- :fill="element.fill" :fit="element.fit" :position="element.position"
12
- :rounded="element.rounded" :href="element.href" :target="element.target"
13
- container-class="w-full h-full min-h-0" :container-style="{}" :style="element.style" />
14
- </LuminaElement>
15
-
16
- <!-- Video Element -->
17
- <LuminaElement v-else-if="element.type === 'video'" :id="elemId(i)"
18
- :class="['flex-item', getSizeClass(element.size), element.class]"
19
- :style="getElementStyle(element)">
20
- <VideoPlayer :class="getRoundedClass(element.rounded, element.fill)" :src="element.src"
21
- :poster="element.poster" :autoplay="element.autoplay ?? false" :loop="element.loop ?? false"
22
- :muted="element.muted ?? false" :controls="element.controls ?? true"
23
- :object-fit="element.fill !== false ? 'cover' : 'contain'" />
24
- </LuminaElement>
25
-
26
- <!-- HTML Element -->
27
- <LuminaElement v-else-if="element.type === 'html'" :id="elemId(i)" :class="['flex-item', getSizeClass(element.size), element.class]" :style="{ ...getElementStyle(element), ...(element.style || {}) }">
28
- <FlexHtml :html="element.html" :class="element.class" :style="element.style" />
29
- </LuminaElement>
30
-
31
- <!-- Content Container (supports nested content) -->
32
- <LuminaElement v-else-if="element.type === 'content'" :id="elemId(i)"
33
- :class="['flex-item flex h-full', element.direction === 'horizontal' ? 'flex-row' : 'flex-col', getSizeClass(element.size), element.class]"
34
- :style="getContentStyle(element)">
35
- <template v-for="(child, j) in element.elements" :key="j">
36
- <!-- Nested content containers (recursive) -->
37
- <LuminaElement v-if="child.type === 'content'" :id="childId(i, j)"
38
- :class="['flex', (child as FlexElementContent).direction === 'horizontal' ? 'flex-row' : 'flex-col']"
39
- :style="getNestedContentStyle(child as FlexElementContent)">
40
- <template v-for="(grandchild, k) in (child as FlexElementContent).elements" :key="k">
41
- <LuminaElement :id="resolveId(props.data, slideIndex.value, ['elements', i, 'elements', j, 'elements', k])">
42
- <!-- Recursively handle nested content -->
43
- <LuminaElement v-if="grandchild.type === 'content'"
44
- :class="['flex', (grandchild as FlexElementContent).direction === 'horizontal' ? 'flex-row' : 'flex-col']"
45
- :style="getNestedContentStyle(grandchild as FlexElementContent)">
46
- <template v-for="(greatGrandchild, l) in (grandchild as FlexElementContent).elements" :key="l">
47
- <LuminaElement :id="resolveId(props.data, slideIndex.value, ['elements', i, 'elements', j, 'elements', k, 'elements', l])">
48
- <component :is="getChildComponent(greatGrandchild.type)" v-bind="greatGrandchild" @action="handleAction" />
49
- </LuminaElement>
50
- </template>
51
- </LuminaElement>
52
- <!-- Regular grandchild elements -->
53
- <component v-else :is="getChildComponent(grandchild.type)" v-bind="grandchild" @action="handleAction" />
54
- </LuminaElement>
55
- </template>
56
- </LuminaElement>
57
- <!-- Regular child elements -->
58
- <LuminaElement v-else :id="childId(i, j)">
59
- <component :is="getChildComponent(child.type)" v-bind="child" @action="handleAction" />
60
- </LuminaElement>
61
- </template>
62
- </LuminaElement>
63
-
64
- <!-- Top-level elements (title, text, bullets, etc.) -->
65
- <LuminaElement v-else :id="elemId(i)" :class="['flex-item', getSizeClass(element.size)]"
66
- :style="{ ...getTopLevelStyle(), width: data.direction === 'vertical' ? '100%' : 'auto' }">
67
- <component :is="getChildComponent(element.type)" v-bind="element" @action="handleAction" />
68
- </LuminaElement>
69
- </template>
70
- </div>
71
- </div>
72
- </BaseSlide>
73
- </template>
74
-
75
- <script setup lang="ts">
76
- import { computed, inject } from 'vue';
77
- import BaseSlide from '../base/BaseSlide.vue';
78
- import VideoPlayer from '../base/VideoPlayer.vue';
79
- import { resolveId } from '../../core/elementResolver';
80
- import { StoreKey } from '../../core/store';
81
- import type { SlideFlex, FlexElement, FlexSize, FlexElementContent } from '../../core/types';
82
- import FlexImage from '../parts/FlexImage.vue';
83
- import FlexTitle from '../parts/FlexTitle.vue';
84
- import FlexText from '../parts/FlexText.vue';
85
- import FlexButton from '../parts/FlexButton.vue';
86
- import FlexBullets from '../parts/FlexBullets.vue';
87
- import FlexOrdered from '../parts/FlexOrdered.vue';
88
- import FlexTimeline from '../parts/FlexTimeline.vue';
89
- import FlexStepper from '../parts/FlexStepper.vue';
90
- import FlexSpacer from '../parts/FlexSpacer.vue';
91
- import FlexHtml from '../parts/FlexHtml.vue';
92
- import { bus } from '../../core/events';
93
- import {
94
- spacingVarMap,
95
- getSizeClass,
96
- getFlexItemStyle,
97
- getFlexContainerStyle,
98
- getTopLevelStyle,
99
- getFlexSlideMainStyle,
100
- getRoundedClass
101
- } from '../../composables/useFlexLayout';
102
-
103
- const props = defineProps<{
104
- data: SlideFlex;
105
- slideIndex?: number;
106
- }>();
107
-
108
- const emit = defineEmits(['action']);
109
- const store = inject(StoreKey);
110
- const slideIndex = computed(() => props.slideIndex ?? store?.state.currentIndex ?? 0);
111
- const elemId = (i: number) => resolveId(props.data, slideIndex.value, ['elements', i]);
112
- const childId = (i: number, j: number) => resolveId(props.data, slideIndex.value, ['elements', i, 'elements', j]);
113
-
114
- const containerStyle = computed(() => ({
115
- padding: spacingVarMap[props.data.padding || 'none'],
116
- }));
117
-
118
- const directionClass = computed(() =>
119
- props.data.direction === 'vertical' ? 'flex-col' : 'flex-col lg:flex-row'
120
- );
121
-
122
- const mainFlexStyle = computed(() => ({
123
- gap: spacingVarMap[props.data.gap || 'none'],
124
- ...getFlexSlideMainStyle(props.data.direction, props.data.halign, props.data.valign),
125
- }));
126
-
127
- // Wrapper for shared logic requiring vertical flag
128
- const getElementStyle = (element: FlexElement & { size?: FlexSize }) => {
129
- return getFlexItemStyle(element, props.data.direction === 'vertical');
130
- };
131
-
132
- const getContentStyle = (element: FlexElementContent & { size?: FlexSize }) => {
133
- const isVertical = props.data.direction === 'vertical';
134
- const contentDirection = element.direction || 'vertical';
135
- return {
136
- ...getFlexItemStyle(element, isVertical),
137
- ...getFlexContainerStyle(element),
138
- // Force full height/width for alignment space
139
- height: isVertical ? 'auto' : '100%',
140
- width: isVertical ? '100%' : 'auto',
141
- minHeight: isVertical ? 'auto' : (contentDirection === 'vertical' ? '100%' : 'auto'),
142
- ...(element.style || {})
143
- };
144
- };
145
-
146
- const getNestedContentStyle = (element: FlexElementContent) => {
147
- return {
148
- ...getFlexContainerStyle(element),
149
- width: '100%',
150
- ...(element.style || {})
151
- };
152
- };
153
-
154
- const handleAction = (payload: any) => {
155
- emit('action', payload);
156
- bus.emit('action', payload);
157
- };
158
-
159
- // ============================================================================
160
- // CHILD ELEMENT COMPONENTS - All using CSS variables
161
- // ============================================================================
162
-
163
-
164
- // Child components extracted to ../parts/
165
-
166
- const getChildComponent = (type: string) => {
167
- const components: Record<string, any> = {
168
- 'title': FlexTitle,
169
- 'text': FlexText,
170
- 'image': FlexImage,
171
- 'video': VideoPlayer,
172
- 'bullets': FlexBullets,
173
- 'ordered': FlexOrdered,
174
- 'button': FlexButton,
175
- 'timeline': FlexTimeline,
176
- 'stepper': FlexStepper,
177
- 'spacer': FlexSpacer,
178
- 'html': FlexHtml,
179
- };
180
- return components[type] || 'div';
181
- };
182
- </script>
183
-
184
- <style scoped>
185
- .flex-layout {
186
- min-height: 100vh;
187
- }
188
-
189
- @media (max-width: 1023px) {
190
- .flex-item {
191
- flex: 0 0 auto !important;
192
- width: 100% !important;
193
- max-width: 100% !important;
194
- max-height: none !important;
195
- min-height: auto !important;
196
- height: auto !important;
197
- }
198
-
199
- /* Images in stacked flex should have a reasonable height */
200
- .flex-item:has(img),
201
- .flex-item img {
202
- height: 40vh !important;
203
- min-height: 300px !important;
204
- }
205
-
206
- .flex-layout {
207
- overflow-y: auto !important;
208
- height: auto !important;
209
- min-height: 100vh;
210
- }
211
- }
212
- </style>
1
+ <template>
2
+ <BaseSlide :data="data" :slide-index="slideIndex">
3
+ <div class="flex-layout w-full h-screen flex flex-col overflow-y-auto lg:overflow-hidden"
4
+ :style="containerStyle">
5
+ <div :class="['flex-1 flex w-full min-h-full lg:h-full', directionClass]"
6
+ :style="mainFlexStyle">
7
+ <template v-for="(element, i) in data.elements" :key="i">
8
+ <!-- Image Element -->
9
+ <LuminaElement v-if="element.type === 'image'" :id="elemId(i)" :class="['flex-item', getSizeClass(element.size), element.class]" :style="{ ...getElementStyle(element), ...(element.style || {}) }">
10
+ <FlexImage :src="element.src" :alt="element.alt"
11
+ :fill="element.fill" :fit="element.fit" :position="element.position"
12
+ :rounded="element.rounded" :href="element.href" :target="element.target"
13
+ container-class="w-full h-full min-h-0" :container-style="{}" :style="element.style" />
14
+ </LuminaElement>
15
+
16
+ <!-- Video Element -->
17
+ <LuminaElement v-else-if="element.type === 'video'" :id="elemId(i)"
18
+ :class="['flex-item', getSizeClass(element.size), element.class]"
19
+ :style="getElementStyle(element)">
20
+ <VideoPlayer :class="getRoundedClass(element.rounded, element.fill)" :src="element.src"
21
+ :poster="element.poster" :autoplay="element.autoplay ?? false" :loop="element.loop ?? false"
22
+ :muted="element.muted ?? false" :controls="element.controls ?? true"
23
+ :object-fit="element.fill !== false ? 'cover' : 'contain'" />
24
+ </LuminaElement>
25
+
26
+ <!-- HTML Element -->
27
+ <LuminaElement v-else-if="element.type === 'html'" :id="elemId(i)" :class="['flex-item', getSizeClass(element.size), element.class]" :style="{ ...getElementStyle(element), ...(element.style || {}) }">
28
+ <FlexHtml :html="element.html" :class="element.class" :style="element.style" />
29
+ </LuminaElement>
30
+
31
+ <!-- Content Container (supports nested content) -->
32
+ <LuminaElement v-else-if="element.type === 'content'" :id="elemId(i)"
33
+ :class="['flex-item flex h-full', element.direction === 'horizontal' ? 'flex-row' : 'flex-col', getSizeClass(element.size), element.class]"
34
+ :style="getContentStyle(element)">
35
+ <template v-for="(child, j) in element.elements" :key="j">
36
+ <!-- Nested content containers (recursive) -->
37
+ <LuminaElement v-if="child.type === 'content'" :id="childId(i, j)"
38
+ :class="['flex', (child as FlexElementContent).direction === 'horizontal' ? 'flex-row' : 'flex-col']"
39
+ :style="getNestedContentStyle(child as FlexElementContent)">
40
+ <template v-for="(grandchild, k) in (child as FlexElementContent).elements" :key="k">
41
+ <LuminaElement :id="resolveId(props.data, slideIndex.value, ['elements', i, 'elements', j, 'elements', k])">
42
+ <!-- Recursively handle nested content -->
43
+ <LuminaElement v-if="grandchild.type === 'content'"
44
+ :class="['flex', (grandchild as FlexElementContent).direction === 'horizontal' ? 'flex-row' : 'flex-col']"
45
+ :style="getNestedContentStyle(grandchild as FlexElementContent)">
46
+ <template v-for="(greatGrandchild, l) in (grandchild as FlexElementContent).elements" :key="l">
47
+ <LuminaElement :id="resolveId(props.data, slideIndex.value, ['elements', i, 'elements', j, 'elements', k, 'elements', l])">
48
+ <component :is="getChildComponent(greatGrandchild.type)" v-bind="greatGrandchild" @action="handleAction" />
49
+ </LuminaElement>
50
+ </template>
51
+ </LuminaElement>
52
+ <!-- Regular grandchild elements -->
53
+ <component v-else :is="getChildComponent(grandchild.type)" v-bind="grandchild" @action="handleAction" />
54
+ </LuminaElement>
55
+ </template>
56
+ </LuminaElement>
57
+ <!-- Regular child elements -->
58
+ <LuminaElement v-else :id="childId(i, j)">
59
+ <component :is="getChildComponent(child.type)" v-bind="child" @action="handleAction" />
60
+ </LuminaElement>
61
+ </template>
62
+ </LuminaElement>
63
+
64
+ <!-- Top-level elements (title, text, bullets, etc.) -->
65
+ <LuminaElement v-else :id="elemId(i)" :class="['flex-item', getSizeClass(element.size)]"
66
+ :style="{ ...getTopLevelStyle(), width: data.direction === 'vertical' ? '100%' : 'auto' }">
67
+ <component :is="getChildComponent(element.type)" v-bind="element" @action="handleAction" />
68
+ </LuminaElement>
69
+ </template>
70
+ </div>
71
+ </div>
72
+ </BaseSlide>
73
+ </template>
74
+
75
+ <script setup lang="ts">
76
+ import { computed, inject } from 'vue';
77
+ import BaseSlide from '../base/BaseSlide.vue';
78
+ import VideoPlayer from '../base/VideoPlayer.vue';
79
+ import { resolveId } from '../../core/elementResolver';
80
+ import { StoreKey } from '../../core/store';
81
+ import type { SlideFlex, FlexElement, FlexSize, FlexElementContent } from '../../core/types';
82
+ import FlexImage from '../parts/FlexImage.vue';
83
+ import FlexTitle from '../parts/FlexTitle.vue';
84
+ import FlexText from '../parts/FlexText.vue';
85
+ import FlexButton from '../parts/FlexButton.vue';
86
+ import FlexBullets from '../parts/FlexBullets.vue';
87
+ import FlexOrdered from '../parts/FlexOrdered.vue';
88
+ import FlexTimeline from '../parts/FlexTimeline.vue';
89
+ import FlexStepper from '../parts/FlexStepper.vue';
90
+ import FlexSpacer from '../parts/FlexSpacer.vue';
91
+ import FlexHtml from '../parts/FlexHtml.vue';
92
+ import { bus } from '../../core/events';
93
+ import {
94
+ spacingVarMap,
95
+ getSizeClass,
96
+ getFlexItemStyle,
97
+ getFlexContainerStyle,
98
+ getTopLevelStyle,
99
+ getFlexSlideMainStyle,
100
+ getRoundedClass
101
+ } from '../../composables/useFlexLayout';
102
+
103
+ const props = defineProps<{
104
+ data: SlideFlex;
105
+ slideIndex?: number;
106
+ }>();
107
+
108
+ const emit = defineEmits(['action']);
109
+ const store = inject(StoreKey);
110
+ const slideIndex = computed(() => props.slideIndex ?? store?.state.currentIndex ?? 0);
111
+ const elemId = (i: number) => resolveId(props.data, slideIndex.value, ['elements', i]);
112
+ const childId = (i: number, j: number) => resolveId(props.data, slideIndex.value, ['elements', i, 'elements', j]);
113
+
114
+ const containerStyle = computed(() => ({
115
+ padding: spacingVarMap[props.data.padding || 'none'],
116
+ }));
117
+
118
+ const directionClass = computed(() =>
119
+ props.data.direction === 'vertical' ? 'flex-col' : 'flex-col lg:flex-row'
120
+ );
121
+
122
+ const mainFlexStyle = computed(() => ({
123
+ gap: spacingVarMap[props.data.gap || 'none'],
124
+ ...getFlexSlideMainStyle(props.data.direction, props.data.halign, props.data.valign),
125
+ }));
126
+
127
+ // Wrapper for shared logic requiring vertical flag
128
+ const getElementStyle = (element: FlexElement & { size?: FlexSize }) => {
129
+ return getFlexItemStyle(element, props.data.direction === 'vertical');
130
+ };
131
+
132
+ const getContentStyle = (element: FlexElementContent & { size?: FlexSize }) => {
133
+ const isVertical = props.data.direction === 'vertical';
134
+ const contentDirection = element.direction || 'vertical';
135
+ return {
136
+ ...getFlexItemStyle(element, isVertical),
137
+ ...getFlexContainerStyle(element),
138
+ // Force full height/width for alignment space
139
+ height: isVertical ? 'auto' : '100%',
140
+ width: isVertical ? '100%' : 'auto',
141
+ minHeight: isVertical ? 'auto' : (contentDirection === 'vertical' ? '100%' : 'auto'),
142
+ ...(element.style || {})
143
+ };
144
+ };
145
+
146
+ const getNestedContentStyle = (element: FlexElementContent) => {
147
+ return {
148
+ ...getFlexContainerStyle(element),
149
+ width: '100%',
150
+ ...(element.style || {})
151
+ };
152
+ };
153
+
154
+ const handleAction = (payload: any) => {
155
+ emit('action', payload);
156
+ bus.emit('action', payload);
157
+ };
158
+
159
+ // ============================================================================
160
+ // CHILD ELEMENT COMPONENTS - All using CSS variables
161
+ // ============================================================================
162
+
163
+
164
+ // Child components extracted to ../parts/
165
+
166
+ const getChildComponent = (type: string) => {
167
+ const components: Record<string, any> = {
168
+ 'title': FlexTitle,
169
+ 'text': FlexText,
170
+ 'image': FlexImage,
171
+ 'video': VideoPlayer,
172
+ 'bullets': FlexBullets,
173
+ 'ordered': FlexOrdered,
174
+ 'button': FlexButton,
175
+ 'timeline': FlexTimeline,
176
+ 'stepper': FlexStepper,
177
+ 'spacer': FlexSpacer,
178
+ 'html': FlexHtml,
179
+ };
180
+ return components[type] || 'div';
181
+ };
182
+ </script>
183
+
184
+ <style scoped>
185
+ .flex-layout {
186
+ min-height: 100vh;
187
+ }
188
+
189
+ @media (max-width: 1023px) {
190
+ .flex-item {
191
+ flex: 0 0 auto !important;
192
+ width: 100% !important;
193
+ max-width: 100% !important;
194
+ max-height: none !important;
195
+ min-height: auto !important;
196
+ height: auto !important;
197
+ }
198
+
199
+ /* Images in stacked flex should have a reasonable height */
200
+ .flex-item:has(img),
201
+ .flex-item img {
202
+ height: 40vh !important;
203
+ min-height: 300px !important;
204
+ }
205
+
206
+ .flex-layout {
207
+ overflow-y: auto !important;
208
+ height: auto !important;
209
+ min-height: 100vh;
210
+ }
211
+ }
212
+ </style>
@@ -3,9 +3,10 @@
3
3
  <div
4
4
  :class="['flex flex-col justify-center items-center text-center relative', data.sizing === 'container' ? 'h-full' : 'min-h-screen']"
5
5
  :style="{ paddingTop: 'var(--lumina-space-xl)', paddingLeft: 'var(--lumina-space-xl)', paddingRight: 'var(--lumina-space-xl)', paddingBottom: 'calc(var(--lumina-space-xl) + var(--lumina-safe-area-bottom))' }">
6
- <div :class="[data.class || '', 'max-w-5xl z-10']">
6
+ <div :class="[data.class || '', 'flex flex-col items-center max-w-5xl z-10']">
7
7
  <!-- Tag -->
8
- <LuminaElement v-if="data.tag" :id="tagId" tag="span" class="inline-block rounded-full font-medium uppercase" :style="{
8
+ <LuminaElement v-if="data.tag" :id="tagId" tag="span" class="rounded-full font-medium uppercase" :style="{
9
+ display: 'inline-block',
9
10
  color: 'var(--lumina-color-primary)',
10
11
  border: '1px solid var(--lumina-color-border)',
11
12
  backgroundColor: 'rgba(var(--lumina-color-text-rgb), 0.05)',
@@ -24,6 +25,7 @@
24
25
 
25
26
  <!-- Subtitle -->
26
27
  <LuminaElement v-if="data.subtitle" :id="subtitleId" tag="p" class="max-w-3xl mx-auto" :style="{
28
+ display: 'block',
27
29
  color: 'var(--lumina-color-text-safe, var(--lumina-color-text))',
28
30
  opacity: 0.8,
29
31
  fontWeight: 'normal',
@@ -57,6 +59,7 @@ const titleId = computed(() => resolveId(props.data, slideIndex.value, ['title']
57
59
  const subtitleId = computed(() => resolveId(props.data, slideIndex.value, ['subtitle']));
58
60
 
59
61
  const titleStyle = computed(() => ({
62
+ display: 'block',
60
63
  fontSize: props.data.sizing === 'container'
61
64
  ? 'var(--lumina-text-5xl)'
62
65
  : 'clamp(var(--lumina-text-4xl), 10vw, var(--lumina-text-7xl))',