Compare commits
4 commits
d91d20ee9a
...
1ced09e728
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ced09e728 | |||
| 75470ea7f3 | |||
| c2a3015fef | |||
| 10bad148ea |
3 changed files with 63 additions and 9 deletions
BIN
src/assets/fonts/eileen-caps-black.woff2
Normal file
BIN
src/assets/fonts/eileen-caps-black.woff2
Normal file
Binary file not shown.
|
|
@ -16,6 +16,17 @@
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Trial hero-title face: "Eileen Caps Black" (D. Rakowski, freeware via
|
||||||
|
fontspace.com/eileen-caps-font-f2027). Caps-only-ish display face with no
|
||||||
|
circumflex glyph, so the hero text drops the accent on "Château". */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Eileen Caps Black';
|
||||||
|
src: url('./assets/fonts/eileen-caps-black.woff2') format('woff2');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Brand palette: deep forest green dominant background, cream
|
/* Brand palette: deep forest green dominant background, cream
|
||||||
foreground, gold (Aboreto/calligraphic spirit) for CTAs and accents. */
|
foreground, gold (Aboreto/calligraphic spirit) for CTAs and accents. */
|
||||||
|
|
@ -99,7 +110,7 @@
|
||||||
'Roboto', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
'Roboto', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||||
--font-display: 'Aboreto', 'Cormorant Garamond', Georgia, serif;
|
--font-display: 'Aboreto', 'Cormorant Garamond', Georgia, serif;
|
||||||
--font-script: 'Tangerine', 'Allura', cursive;
|
--font-script: 'Tangerine', 'Allura', cursive;
|
||||||
--font-title: 'Berry Rotunda', 'UnifrakturMaguntia', serif;
|
--font-title: 'Eileen Caps Black', 'Berry Rotunda', 'UnifrakturMaguntia', serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<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 { useI18n } from 'vue-i18n'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
|
@ -21,7 +21,7 @@ const tileStyle = {
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
// The hero title is SVG text (Berry Rotunda) used as a clip over the
|
// The hero title is SVG text (Eileen Caps Black) used as a clip over the
|
||||||
// wordmark video. Measure the rendered glyph box once the font loads and fit
|
// wordmark video. Measure the rendered glyph box once the font loads and fit
|
||||||
// the viewBox/foreignObject to it, so the wordmark fills the SVG width without
|
// the viewBox/foreignObject to it, so the wordmark fills the SVG width without
|
||||||
// clipping regardless of the font's metrics.
|
// clipping regardless of the font's metrics.
|
||||||
|
|
@ -32,7 +32,23 @@ const titleViewBox = computed(
|
||||||
() => `${titleBox.value.x} ${titleBox.value.y} ${titleBox.value.w} ${titleBox.value.h}`,
|
() => `${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 {
|
try {
|
||||||
await (document as Document & { fonts?: FontFaceSet }).fonts?.ready
|
await (document as Document & { fonts?: FontFaceSet }).fonts?.ready
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -48,14 +64,30 @@ onMounted(async () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!bb.width) return
|
if (!bb.width) return
|
||||||
const padX = bb.height * 0.08
|
const lineH = bb.height / (titleStacked.value ? titleWords.length : 1)
|
||||||
const padY = bb.height * 0.16
|
const padX = lineH * 0.08
|
||||||
|
const padY = lineH * 0.16
|
||||||
titleBox.value = {
|
titleBox.value = {
|
||||||
x: Math.round(bb.x - padX),
|
x: Math.round(bb.x - padX),
|
||||||
y: Math.round(bb.y - padY),
|
y: Math.round(bb.y - padY),
|
||||||
w: Math.round(bb.width + padX * 2),
|
w: Math.round(bb.width + padX * 2),
|
||||||
h: Math.round(bb.height + padY * 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 = [
|
const pathways = [
|
||||||
|
|
@ -116,7 +148,7 @@ const featuredEvents = [
|
||||||
(always one line); a gold drop-shadow gives the engraved edge. -->
|
(always one line); a gold drop-shadow gives the engraved edge. -->
|
||||||
<svg
|
<svg
|
||||||
ref="titleSvg"
|
ref="titleSvg"
|
||||||
class="hero-title -mt-1 block w-[min(92vw,52rem)] sm:mt-4"
|
class="hero-title -mt-1 block w-full max-w-[68rem] sm:mt-4"
|
||||||
:viewBox="titleViewBox"
|
:viewBox="titleViewBox"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
|
|
@ -127,10 +159,21 @@ const featuredEvents = [
|
||||||
x="550"
|
x="550"
|
||||||
y="122"
|
y="122"
|
||||||
text-anchor="middle"
|
text-anchor="middle"
|
||||||
font-family="'Berry Rotunda', 'UnifrakturMaguntia', serif"
|
font-family="'Eileen Caps Black', 'Berry Rotunda', 'UnifrakturMaguntia', serif"
|
||||||
font-size="120"
|
font-size="120"
|
||||||
>
|
>
|
||||||
Château 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>
|
</text>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue