Hero title: trial Eileen Caps Black, larger, stacked on phones #4
1 changed files with 48 additions and 5 deletions
feat(hero): stack the title one word per line on phones
Below the sm breakpoint the wordmark renders as three centered tspan lines (Chateau / du / Faune) with "du" at half size, instead of a thin one-line strip. Line offsets are absolute SVG units so the gap above and below the small line is equal. A matchMedia listener flips the layout and re-runs the clip-box measurement so the video clip refits when it changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
commit
75470ea7f3
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
|
@ -32,7 +32,23 @@ const titleViewBox = computed(
|
|||
() => `${titleBox.value.x} ${titleBox.value.y} ${titleBox.value.w} ${titleBox.value.h}`,
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
// Below the `sm` breakpoint the three words stack one per line (the wide
|
||||
// caps face otherwise renders as a thin strip on phones); from sm up they sit
|
||||
// on one line. Re-measure whenever the layout flips.
|
||||
const titleWords = ['Chateau', 'du', 'Faune']
|
||||
// Stacked layout: "du" at half size. Baseline offsets are absolute user
|
||||
// units (not em, which would scale with each line's own size) chosen so the
|
||||
// gap above and below the small line is the same.
|
||||
const titleLineSize = [120, 60, 120]
|
||||
const titleLineDy = [0, 83, 133]
|
||||
const stackedQuery = '(max-width: 639px)'
|
||||
const titleStacked = ref(false)
|
||||
let stackedMql: MediaQueryList | null = null
|
||||
const onStackedChange = (e: MediaQueryListEvent) => {
|
||||
titleStacked.value = e.matches
|
||||
}
|
||||
|
||||
async function measureTitle() {
|
||||
try {
|
||||
await (document as Document & { fonts?: FontFaceSet }).fonts?.ready
|
||||
} catch {
|
||||
|
|
@ -48,14 +64,30 @@ onMounted(async () => {
|
|||
return
|
||||
}
|
||||
if (!bb.width) return
|
||||
const padX = bb.height * 0.08
|
||||
const padY = bb.height * 0.16
|
||||
const lineH = bb.height / (titleStacked.value ? titleWords.length : 1)
|
||||
const padX = lineH * 0.08
|
||||
const padY = lineH * 0.16
|
||||
titleBox.value = {
|
||||
x: Math.round(bb.x - padX),
|
||||
y: Math.round(bb.y - padY),
|
||||
w: Math.round(bb.width + padX * 2),
|
||||
h: Math.round(bb.height + padY * 2),
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
stackedMql = window.matchMedia(stackedQuery)
|
||||
titleStacked.value = stackedMql.matches
|
||||
stackedMql.addEventListener('change', onStackedChange)
|
||||
measureTitle()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stackedMql?.removeEventListener('change', onStackedChange)
|
||||
})
|
||||
|
||||
watch(titleStacked, () => {
|
||||
measureTitle()
|
||||
})
|
||||
|
||||
const pathways = [
|
||||
|
|
@ -130,7 +162,18 @@ const featuredEvents = [
|
|||
font-family="'Eileen Caps Black', 'Berry Rotunda', 'UnifrakturMaguntia', serif"
|
||||
font-size="120"
|
||||
>
|
||||
Chateau du Faune
|
||||
<template v-if="titleStacked">
|
||||
<tspan
|
||||
v-for="(word, i) in titleWords"
|
||||
:key="word"
|
||||
x="550"
|
||||
:dy="titleLineDy[i]"
|
||||
:font-size="titleLineSize[i]"
|
||||
>
|
||||
{{ word }}
|
||||
</tspan>
|
||||
</template>
|
||||
<template v-else>{{ titleWords.join(' ') }}</template>
|
||||
</text>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue