kmcom-nuxt-layers 6.0.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/docs/USAGE.md +8 -8
  2. package/docs/adr/0003-modular-step-system-replaces-fluid-clamps.md +58 -0
  3. package/layers/core/app/app.config.ts +6 -3
  4. package/layers/core/app/assets/css/tokens/spacing-fluid.css +24 -19
  5. package/layers/core/app/components/Element.test.ts +2 -2
  6. package/layers/core/app/components/LoadingScreen.vue +142 -76
  7. package/layers/core/app/composables/useLayoutAttrs.test.ts +35 -5
  8. package/layers/core/app/composables/useLayoutAttrs.ts +1 -1
  9. package/layers/core/app/composables/useLoading.ts +57 -36
  10. package/layers/core/app/plugins/loading.client.ts +21 -27
  11. package/layers/core/app/types/loading.ts +18 -8
  12. package/layers/core/app/utils/index.ts +1 -0
  13. package/layers/core/app/utils/loading.test.ts +48 -0
  14. package/layers/core/app/utils/loading.ts +53 -0
  15. package/layers/core/app/utils/resolveElementStyle.test.ts +2 -2
  16. package/layers/design-system/theming/app/types/designSystem.ts +21 -17
  17. package/layers/design-system/typography/app/app.config.ts +26 -0
  18. package/layers/design-system/typography/app/assets/css/typography.css +216 -187
  19. package/layers/design-system/typography/app/components/Typography/CodeBlock.vue +4 -4
  20. package/layers/design-system/typography/app/components/Typography/Headline.test.ts +36 -0
  21. package/layers/design-system/typography/app/components/Typography/Headline.vue +20 -21
  22. package/layers/design-system/typography/app/components/Typography/QuoteBlock.vue +4 -4
  23. package/layers/design-system/typography/app/components/Typography/RepeatText.vue +7 -6
  24. package/layers/design-system/typography/app/components/Typography/index.test.ts +31 -0
  25. package/layers/design-system/typography/app/components/Typography/index.vue +10 -8
  26. package/layers/design-system/typography/app/composables/typography.test.ts +53 -0
  27. package/layers/design-system/typography/app/composables/typography.ts +28 -43
  28. package/layers/design-system/typography/app/types/typography.ts +5 -32
  29. package/layers/motion/scroll/app/plugins/locomotive-scroll.client.ts +22 -0
  30. package/layers/structure/grid/app/assets/css/grid.css +11 -7
  31. package/package.json +1 -1
@@ -1,9 +1,12 @@
1
1
  <script lang="ts" setup>
2
+ // Explicit import so Headline mounts under vitest's 'vue' project (no Nuxt auto-imports)
3
+ // — same exemption as ElementTw.vue (eslint.config.mjs).
4
+ import { computed } from 'vue'
5
+
2
6
  import { useColor } from '../../composables/color'
3
7
  import { useTypography } from '../../composables/typography'
4
8
  import type { UiColors } from '../../types/colors'
5
9
  import type {
6
- FluidFontSize,
7
10
  FontFamily,
8
11
  FontLeading,
9
12
  FontSize,
@@ -13,6 +16,7 @@
13
16
  FontWidth,
14
17
  TextAlign,
15
18
  TextTransform,
19
+ TypeStep,
16
20
  } from '../../types/typography'
17
21
 
18
22
  const {
@@ -20,13 +24,13 @@
20
24
  weight = 'font-bold',
21
25
  width = 'font-stretch-normal',
22
26
  slant = 'normal',
23
- leading = 'leading-tight',
27
+ leading = undefined,
24
28
  tracking = 'tracking-tight',
25
29
  align = 'left',
26
30
  transform = 'none',
27
31
  color = undefined,
28
32
  size = undefined,
29
- fluidSize = undefined,
33
+ step = undefined,
30
34
  font = undefined,
31
35
  } = defineProps<{
32
36
  level?: 1 | 2 | 3 | 4 | 5 | 6
@@ -39,43 +43,38 @@
39
43
  transform?: TextTransform
40
44
  color?: UiColors
41
45
  size?: FontSize
42
- fluidSize?: FluidFontSize
46
+ step?: TypeStep
43
47
  font?: FontFamily
44
48
  }>()
45
49
  const tag = computed(() => `h${level}` as const)
46
50
 
47
- // Fluid by default (continuous scaling, no mobile-first breakpoint step) —
48
- // only used when the caller passes neither `size` nor `fluidSize`.
49
- const defaultFluidSizes: Record<number, FluidFontSize> = {
50
- 1: '6xl',
51
- 2: '5xl',
52
- 3: '4xl',
53
- 4: '3xl',
54
- 5: 'xl',
55
- 6: 'lg',
56
- }
57
-
58
- const appliedFluidSize = computed(() =>
59
- size !== undefined ? undefined : (fluidSize ?? defaultFluidSizes[level])
60
- )
51
+ // Display hierarchy by default: each level sits one scale step below the previous, starting
52
+ // at the fluid display step, so every level stays fluid (h1 51 → 97px, h2 40 → 77px …). Set as
53
+ // --step rather than a class so typography.css's h1–h6 base rules size it. Only when neither
54
+ // `step` nor `size` is passed; an explicit step beats size.
55
+ const defaultStepStyle = computed(() => {
56
+ if (step !== undefined || size !== undefined) return undefined
57
+ const below = level - 1
58
+ return { '--step': below ? `calc(var(--step-display) - ${below})` : 'var(--step-display)' }
59
+ })
61
60
 
62
61
  const { classes } = useTypography(() => ({
63
62
  weight,
64
63
  width,
65
64
  slant,
66
- leading,
65
+ ...(leading !== undefined && { leading }),
67
66
  tracking,
68
67
  align,
69
68
  transform,
70
69
  ...(font !== undefined && { font }),
71
70
  ...(size !== undefined && { size }),
72
- ...(appliedFluidSize.value !== undefined && { fluidSize: appliedFluidSize.value }),
71
+ ...(step !== undefined && { step }),
73
72
  }))
74
73
  const colorClass = useColor(() => color, 'text')
75
74
  </script>
76
75
 
77
76
  <template>
78
- <component :is="tag" :class="[classes, colorClass]">
77
+ <component :is="tag" :class="[classes, colorClass]" :style="defaultStepStyle">
79
78
  <slot />
80
79
  </component>
81
80
  </template>
@@ -3,7 +3,6 @@
3
3
  import { useTypography } from '../../composables/typography'
4
4
  import type { UiColors } from '../../types/colors'
5
5
  import type {
6
- FluidFontSize,
7
6
  FontFamily,
8
7
  FontLeading,
9
8
  FontSize,
@@ -13,6 +12,7 @@
13
12
  FontWidth,
14
13
  TextAlign,
15
14
  TextTransform,
15
+ TypeStep,
16
16
  } from '../../types/typography'
17
17
 
18
18
  const {
@@ -25,7 +25,7 @@
25
25
  transform = 'none',
26
26
  color = undefined,
27
27
  size = undefined,
28
- fluidSize = undefined,
28
+ step = undefined,
29
29
  font = undefined,
30
30
  } = defineProps<{
31
31
  weight?: FontWeight
@@ -37,7 +37,7 @@
37
37
  transform?: TextTransform
38
38
  color?: UiColors
39
39
  size?: FontSize
40
- fluidSize?: FluidFontSize
40
+ step?: TypeStep
41
41
  font?: FontFamily
42
42
  }>()
43
43
  const { classes } = useTypography(() => ({
@@ -50,7 +50,7 @@
50
50
  transform,
51
51
  ...(font !== undefined && { font }),
52
52
  ...(size !== undefined && { size }),
53
- ...(fluidSize !== undefined && { fluidSize }),
53
+ ...(step !== undefined && { step }),
54
54
  }))
55
55
  const colorClass = useColor(() => color, 'text')
56
56
  </script>
@@ -3,7 +3,7 @@
3
3
  import { useRepeatFill } from '../../composables/repeatFill'
4
4
  import { useTypography } from '../../composables/typography'
5
5
  import type { UiColors } from '../../types/colors'
6
- import type { FluidFontSize, FontWeight, TypographyTag } from '../../types/typography'
6
+ import type { FontWeight, TypeStep, TypographyTag } from '../../types/typography'
7
7
 
8
8
  const {
9
9
  rows = 1,
@@ -12,7 +12,7 @@
12
12
  repeat = 'auto',
13
13
  separator = '',
14
14
  weight = undefined,
15
- size = '4xl',
15
+ step = 7,
16
16
  color = undefined,
17
17
  offset = [],
18
18
  letterSpacing = undefined,
@@ -28,7 +28,7 @@
28
28
  repeat?: number | 'auto'
29
29
  separator?: string
30
30
  weight?: FontWeight | FontWeight[]
31
- size?: FluidFontSize | FluidFontSize[]
31
+ step?: TypeStep | TypeStep[]
32
32
  color?: UiColors | UiColors[]
33
33
  offset?: string[]
34
34
  letterSpacing?: string | string[]
@@ -49,7 +49,7 @@
49
49
 
50
50
  const rowText = (i: number) => pick(text, i) ?? ''
51
51
  const rowWeight = (i: number) => pick(weight, i)
52
- const rowSize = (i: number) => pick(size, i)
52
+ const rowStep = (i: number) => pick(step, i)
53
53
  const rowColor = (i: number) => pick(color, i)
54
54
  const rowOffset = (i: number) => offset[i] ?? '0'
55
55
  const rowLetterSpacing = (i: number) => pick(letterSpacing, i)
@@ -58,10 +58,11 @@
58
58
  const rowClasses = computed(() =>
59
59
  Array.from({ length: rows }, (_, i) => {
60
60
  const weight = rowWeight(i)
61
- const fluidSize = rowSize(i)
61
+ const step = rowStep(i)
62
62
  return useTypography({
63
+ leading: 'leading-normal',
63
64
  ...(weight !== undefined && { weight }),
64
- ...(fluidSize !== undefined && { fluidSize }),
65
+ ...(step !== undefined && { step }),
65
66
  }).classes.value
66
67
  })
67
68
  )
@@ -0,0 +1,31 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vite-plus/test'
3
+
4
+ import ElementTw from '#layers/core/app/components/ElementTw.vue'
5
+
6
+ import Typography from './index.vue'
7
+
8
+ const classesOf = (props: Record<string, unknown> = {}) =>
9
+ mount(Typography, {
10
+ props,
11
+ slots: { default: 'Body copy' },
12
+ global: { components: { ElementTw } },
13
+ }).classes()
14
+
15
+ describe('Typography size', () => {
16
+ it('inherits the surrounding size by default instead of pinning text-base', () => {
17
+ expect(classesOf()).not.toContain('text-base')
18
+ })
19
+
20
+ it('inherits the body line height by default', () => {
21
+ expect(classesOf().filter((c) => c.startsWith('leading-'))).toEqual([])
22
+ })
23
+
24
+ it('still applies an explicit size', () => {
25
+ expect(classesOf({ size: 'lg' })).toContain('text-lg')
26
+ })
27
+
28
+ it('applies an explicit step', () => {
29
+ expect(classesOf({ step: 1 })).toContain('text-step-1')
30
+ })
31
+ })
@@ -3,7 +3,6 @@
3
3
  import { useTypography } from '../../composables/typography'
4
4
  import type { UiColors } from '../../types/colors'
5
5
  import type {
6
- FluidFontSize,
7
6
  FontFamily,
8
7
  FontLeading,
9
8
  FontSize,
@@ -13,6 +12,7 @@
13
12
  FontWidth,
14
13
  TextAlign,
15
14
  TextTransform,
15
+ TypeStep,
16
16
  TypographyTag,
17
17
  } from '../../types/typography'
18
18
 
@@ -21,13 +21,15 @@
21
21
  weight = 'font-normal',
22
22
  width = 'font-stretch-normal',
23
23
  slant = 'normal',
24
- leading = 'leading-normal',
24
+ leading = undefined,
25
25
  tracking = 'tracking-normal',
26
26
  align = 'left',
27
27
  transform = 'none',
28
28
  color = undefined,
29
- size = 'base',
30
- fluidSize = undefined,
29
+ // No default size: body copy inherits the body step (20 → 24px) rather than pinning
30
+ // Tailwind's 16px text-base inside it.
31
+ size = undefined,
32
+ step = undefined,
31
33
  font = undefined,
32
34
  } = defineProps<{
33
35
  tag?: TypographyTag
@@ -40,20 +42,20 @@
40
42
  transform?: TextTransform
41
43
  color?: UiColors
42
44
  size?: FontSize
43
- fluidSize?: FluidFontSize
45
+ step?: TypeStep
44
46
  font?: FontFamily
45
47
  }>()
46
48
  const { classes } = useTypography(() => ({
47
49
  weight,
48
50
  width,
49
51
  slant,
50
- leading,
52
+ ...(leading !== undefined && { leading }),
51
53
  tracking,
52
54
  align,
53
55
  transform,
54
- size,
56
+ ...(size !== undefined && { size }),
55
57
  ...(font !== undefined && { font }),
56
- ...(fluidSize !== undefined && { fluidSize }),
58
+ ...(step !== undefined && { step }),
57
59
  }))
58
60
  const colorClass = useColor(() => color, 'text')
59
61
  </script>
@@ -0,0 +1,53 @@
1
+ import { readFileSync } from 'node:fs'
2
+
3
+ import { describe, expect, it } from 'vite-plus/test'
4
+
5
+ import { TYPE_STEP_CLASSES, useTypography } from './typography'
6
+
7
+ // Tailwind only emits a class that appears literally in source AND has an @utility — a map
8
+ // entry without a utility renders unstyled, a utility without an entry is unreachable from the
9
+ // components. Both fail silently, so pin the two lists to each other.
10
+ const css = readFileSync(new URL('../assets/css/typography.css', import.meta.url), 'utf8')
11
+ const utilities = [...css.matchAll(/@utility\s+(text-step-[\w-]+)/g)].map(([, name]) => name)
12
+ const mapped = Object.values(TYPE_STEP_CLASSES)
13
+
14
+ describe('TYPE_STEP_CLASSES ↔ typography.css', () => {
15
+ it('has an @utility for every mapped class', () => {
16
+ for (const cls of mapped) expect(utilities).toContain(cls)
17
+ })
18
+
19
+ it('maps every text-step @utility', () => {
20
+ for (const name of utilities) expect(mapped).toContain(name)
21
+ })
22
+
23
+ it('names each class after its step', () => {
24
+ for (const [step, cls] of Object.entries(TYPE_STEP_CLASSES)) {
25
+ expect(cls).toBe(`text-step-${step}`)
26
+ }
27
+ })
28
+ })
29
+
30
+ describe('useTypography step', () => {
31
+ const classesOf = (props: Parameters<typeof useTypography>[0]) =>
32
+ useTypography(props).classes.value.split(' ')
33
+
34
+ it('emits step 0 rather than treating it as unset', () => {
35
+ expect(classesOf({ step: 0 })).toContain('text-step-0')
36
+ })
37
+
38
+ it('wins over size instead of emitting two font sizes', () => {
39
+ const classes = classesOf({ step: 3, size: 'sm' })
40
+ expect(classes).toContain('text-step-3')
41
+ expect(classes).not.toContain('text-sm')
42
+ })
43
+
44
+ it('falls back to size when no step is given', () => {
45
+ expect(classesOf({ size: 'sm' })).toContain('text-sm')
46
+ })
47
+
48
+ // A leading-* class sorts after text-step-* and would override the step's line height.
49
+ it('emits no leading class unless one is given', () => {
50
+ expect(classesOf({ step: 3 }).filter((c) => c.startsWith('leading-'))).toEqual([])
51
+ expect(classesOf({ step: 3, leading: 'leading-tight' })).toContain('leading-tight')
52
+ })
53
+ })
@@ -1,7 +1,6 @@
1
- import { toValue, type MaybeRefOrGetter } from 'vue'
1
+ import { computed, toValue, type MaybeRefOrGetter } from 'vue'
2
2
 
3
3
  import type {
4
- FluidFontSize,
5
4
  FontFamily,
6
5
  FontLeading,
7
6
  FontSize,
@@ -11,6 +10,7 @@ import type {
11
10
  FontWidth,
12
11
  TextAlign,
13
12
  TextTransform,
13
+ TypeStep,
14
14
  } from '../types/typography'
15
15
 
16
16
  type AxisValue = string | number | undefined
@@ -36,39 +36,25 @@ function getSizeClass(size: FontSize | undefined): string {
36
36
  }
37
37
 
38
38
  // Literal map, not template-literal interpolation — Tailwind's content scanner
39
- // looks for complete class-name strings in source, so `text-fluid-${x}` is
40
- // invisible to it and silently drops the utility.
41
- const FLUID_SIZE_CLASSES: Record<FluidFontSize, string> = {
42
- xs: 'text-fluid-xs',
43
- sm: 'text-fluid-sm',
44
- md: 'text-fluid-md',
45
- lg: 'text-fluid-lg',
46
- xl: 'text-fluid-xl',
47
- '2xl': 'text-fluid-2xl',
48
- '3xl': 'text-fluid-3xl',
49
- '4xl': 'text-fluid-4xl',
50
- '5xl': 'text-fluid-5xl',
51
- '6xl': 'text-fluid-6xl',
52
- '7xl': 'text-fluid-7xl',
53
- '8xl': 'text-fluid-8xl',
54
- '9xl': 'text-fluid-9xl',
55
- 'xs-cq': 'text-fluid-xs-cq',
56
- 'sm-cq': 'text-fluid-sm-cq',
57
- 'md-cq': 'text-fluid-md-cq',
58
- 'lg-cq': 'text-fluid-lg-cq',
59
- 'xl-cq': 'text-fluid-xl-cq',
60
- '2xl-cq': 'text-fluid-2xl-cq',
61
- '3xl-cq': 'text-fluid-3xl-cq',
62
- '4xl-cq': 'text-fluid-4xl-cq',
63
- '5xl-cq': 'text-fluid-5xl-cq',
64
- '6xl-cq': 'text-fluid-6xl-cq',
65
- '7xl-cq': 'text-fluid-7xl-cq',
66
- '8xl-cq': 'text-fluid-8xl-cq',
67
- '9xl-cq': 'text-fluid-9xl-cq',
68
- }
69
-
70
- function getFluidSizeClass(fluidSize: FluidFontSize | undefined): string {
71
- return fluidSize ? FLUID_SIZE_CLASSES[fluidSize] : ''
39
+ // looks for complete class-name strings in source, so `text-step-${x}` is
40
+ // invisible to it and silently drops the utility. Kept in sync with
41
+ // typography.css's @utility list by typography.test.ts.
42
+ export const TYPE_STEP_CLASSES: Record<TypeStep, string> = {
43
+ sm: 'text-step-sm',
44
+ 0: 'text-step-0',
45
+ 1: 'text-step-1',
46
+ 2: 'text-step-2',
47
+ 3: 'text-step-3',
48
+ 4: 'text-step-4',
49
+ 5: 'text-step-5',
50
+ 6: 'text-step-6',
51
+ 7: 'text-step-7',
52
+ 8: 'text-step-8',
53
+ 9: 'text-step-9',
54
+ 10: 'text-step-10',
55
+ 11: 'text-step-11',
56
+ 12: 'text-step-12',
57
+ display: 'text-step-display',
72
58
  }
73
59
 
74
60
  export function useTypography(
@@ -81,7 +67,7 @@ export function useTypography(
81
67
  align?: TextAlign
82
68
  transform?: TextTransform
83
69
  size?: FontSize
84
- fluidSize?: FluidFontSize
70
+ step?: TypeStep
85
71
  font?: FontFamily
86
72
  }>
87
73
  ) {
@@ -103,20 +89,19 @@ export function useTypography(
103
89
  fallback: 'not-italic',
104
90
  }),
105
91
 
106
- normalizeAxis(p.leading, {
107
- prefix: 'leading',
108
- fallback: 'leading-normal',
109
- }),
92
+ // No fallback: a leading-* class sorts after text-step-* and would override the step's
93
+ // line height, so only emit one when asked.
94
+ normalizeAxis(p.leading, { prefix: 'leading' }),
110
95
 
111
96
  normalizeAxis(p.tracking, {
112
97
  prefix: 'tracking',
113
98
  fallback: 'tracking-normal',
114
99
  }),
115
100
 
116
- // fluidSize is the parallel, opt-in namespace (see typography.css) — takes
101
+ // step is the parallel modular-scale namespace (see typography.css) — takes
117
102
  // precedence over size when both are set, rather than emitting two
118
- // conflicting font-size utilities.
119
- p.fluidSize ? getFluidSizeClass(p.fluidSize) : getSizeClass(p.size),
103
+ // conflicting font-size utilities. Checked against undefined: step 0 is valid.
104
+ p.step !== undefined ? TYPE_STEP_CLASSES[p.step] : getSizeClass(p.size),
120
105
 
121
106
  p.align ? `text-${p.align}` : '',
122
107
 
@@ -61,40 +61,13 @@ export type FontSize =
61
61
  | number
62
62
 
63
63
  /* ======================================================
64
- Fluid Font Size
65
- - Stepped clamp() scale, parallel to Tailwind's --text-*
66
- - Companion to text-fluid-* utilities (typography.css)
64
+ Type Step
65
+ - Step on the modular scale: base × 1.26^step, parallel to Tailwind's --text-*
66
+ - Companion to text-step-* utilities (typography.css)
67
+ - 'sm' is step −1; 'display' is fluid, 5 → 7 with viewport width
67
68
  ====================================================== */
68
69
 
69
- export type FluidFontSize =
70
- | 'xs'
71
- | 'sm'
72
- | 'md'
73
- | 'lg'
74
- | 'xl'
75
- | '2xl'
76
- | '3xl'
77
- | '4xl'
78
- | '5xl'
79
- | '6xl'
80
- | '7xl'
81
- | '8xl'
82
- | '9xl'
83
- // Container-relative (cqi) variants — require a `container-type` ancestor
84
- // (e.g. `@container`), otherwise cqi resolves against the small viewport.
85
- | 'xs-cq'
86
- | 'sm-cq'
87
- | 'md-cq'
88
- | 'lg-cq'
89
- | 'xl-cq'
90
- | '2xl-cq'
91
- | '3xl-cq'
92
- | '4xl-cq'
93
- | '5xl-cq'
94
- | '6xl-cq'
95
- | '7xl-cq'
96
- | '8xl-cq'
97
- | '9xl-cq'
70
+ export type TypeStep = 'sm' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 'display'
98
71
 
99
72
  /* ======================================================
100
73
  Font Width
@@ -1,3 +1,4 @@
1
+ import { gsap } from 'gsap'
1
2
  import { ScrollTrigger } from 'gsap/ScrollTrigger'
2
3
  import LocomotiveScroll from 'locomotive-scroll'
3
4
 
@@ -15,10 +16,13 @@ export default defineNuxtPlugin({
15
16
  })
16
17
 
17
18
  const instance = shallowRef<LocomotiveScroll | null>(null)
19
+ const { screen } = useLoading()
18
20
 
19
21
  function init() {
20
22
  if (instance.value) return
21
23
  instance.value = new LocomotiveScroll({
24
+ // Under core's boot loading screen, wait for its exit to start (watcher below)
25
+ autoStart: screen.value !== 'visible',
22
26
  lenisOptions: {
23
27
  lerp: 0.1,
24
28
  smoothWheel: true,
@@ -45,6 +49,24 @@ export default defineNuxtPlugin({
45
49
  instance.value = null
46
50
  }
47
51
 
52
+ // Hold motion until core's boot loading screen reveals the page: GSAP stays paused while the
53
+ // screen is up, so entrance animations play once it's gone; scrolling unlocks as it exits.
54
+ watch(
55
+ screen,
56
+ (state) => {
57
+ gsap.globalTimeline.paused(state !== 'hidden')
58
+
59
+ if (state !== 'leaving') return
60
+ instance.value?.start()
61
+ // Remeasure once the screen's scroll lock is off the page
62
+ requestAnimationFrame(() => {
63
+ instance.value?.resize()
64
+ ScrollTrigger.refresh()
65
+ })
66
+ },
67
+ { immediate: true }
68
+ )
69
+
48
70
  const router = useRouter()
49
71
  const appConfig = useAppConfig()
50
72
  const scrollConfig = appConfig.scroll as { smoothScroll?: boolean | string[] } | undefined
@@ -3,15 +3,19 @@
3
3
  for the full derivation rationale. Column composition is NOT here — it comes from
4
4
  useGridTracks().toColumnsCss(), applied inline per grid-tracks instance. */
5
5
 
6
- @property --fs {
7
- syntax: '<length>';
8
- inherits: true;
9
- initial-value: 1rem;
10
- }
6
+ /* --fs stays unregistered on purpose: a registered <length> holding rem on :root, read by
7
+ :root's own line-height, is a dependency cycle in Gecko -- Firefox falls back to the initial
8
+ value and freezes the unit at 24px. Unregistered, it substitutes and resolves the same in
9
+ every engine. (An `initial-value: 1rem` @property is invalid anyway: rem is not
10
+ computationally independent.) */
11
11
 
12
12
  :root {
13
- /* rem term keeps this WCAG 1.4.4 zoom-safe; svi never vw (viewport-bar-collapse safe) */
14
- --fs: clamp(1rem, 0.95rem + 0.25svi, 1.125rem);
13
+ /* Shares the type scale's endpoints (typography.css --type-min/--type-max) but stays
14
+ continuous across 320 → 1280px, so the unit never takes the type base's 48rem jump.
15
+ Fallbacks keep grid valid on core alone. The slope is hand-fitted to 1rem → 1.2rem over
16
+ 320 → 1280px — retune it if --type-min/--type-max change. rem term keeps this WCAG
17
+ 1.4.4 zoom-safe; svi never vw (viewport-bar-collapse safe) */
18
+ --fs: clamp(var(--type-min, 1rem), 0.9333rem + 0.3333svi, var(--type-max, 1.2rem));
15
19
 
16
20
  /* rounded, not raw, so grid boundaries land on-pixel */
17
21
  line-height: round(nearest, var(--fs) * 1.5, 1px);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kmcom-nuxt-layers",
3
3
  "private": false,
4
- "version": "6.0.0",
4
+ "version": "6.2.0",
5
5
  "description": "Composable Nuxt 4 layers for building scalable Vue applications",
6
6
  "exports": {
7
7
  "./layers/core": "./layers/core/nuxt.config.ts",