Compare commits

...

3 Commits

Author SHA1 Message Date
66039a1306 Add images
All checks were successful
Build and deploy updated apps / Build & deploy (push) Successful in 2m22s
2025-06-15 18:58:38 +02:00
1b18ede387 Add sport shop notice 2025-06-15 16:57:30 +02:00
f79d9e3859 Add icon support to variant switcher 2025-06-15 15:57:35 +02:00
56 changed files with 187 additions and 78 deletions

View File

@ -2,7 +2,7 @@ export default defineAppConfig({
ui: {
colors: {
primary: 'gimblet',
secondary: 'sapphire',
secondary: 'stone',
neutral: 'sandstone'
}
}

View File

@ -41,6 +41,19 @@
--color-gimblet-800: oklch(44.164% 0.05385 91.131);
--color-gimblet-900: oklch(31.471% 0.0355 91.688);
--color-driftwood: oklch(72% 0.065 228);
--color-driftwood-50: oklch(97.5% 0.008 228);
--color-driftwood-100: oklch(94.5% 0.015 228);
--color-driftwood-200: oklch(88.8% 0.028 228);
--color-driftwood-300: oklch(83% 0.042 228);
--color-driftwood-400: oklch(77% 0.056 228);
--color-driftwood-500: oklch(72% 0.065 228);
--color-driftwood-600: oklch(63% 0.07 228);
--color-driftwood-700: oklch(50% 0.06 228);
--color-driftwood-800: oklch(38% 0.045 228);
--color-driftwood-900: oklch(27% 0.03 228);
--color-sapphire-50: oklch(97.5% 0.005 260);
--color-sapphire-100: oklch(95.2% 0.01 260);
--color-sapphire-200: oklch(90% 0.02 260);
@ -52,6 +65,30 @@
--color-sapphire-800: oklch(42% 0.045 260);
--color-sapphire-900: oklch(30% 0.03 260);
--color-stone-50: oklch(98% 0.002 260); /* lightest gray */
--color-stone-100: oklch(94% 0.004 260);
--color-stone-200: oklch(88% 0.006 260);
--color-stone-300: oklch(80% 0.008 260);
--color-stone-400: oklch(70% 0.01 260);
--color-stone-500: oklch(60% 0.012 260); /* true neutral gray */
--color-stone-600: oklch(48% 0.012 260);
--color-stone-700: oklch(36% 0.01 260);
--color-stone-800: oklch(26% 0.008 260);
--color-stone-900: oklch(16% 0.006 260); /* darkest gray */
--color-clay-50: oklch(96% 0.008 35);
--color-clay-100: oklch(92% 0.015 35);
--color-clay-200: oklch(86% 0.03 35);
--color-clay-300: oklch(80% 0.05 35);
--color-clay-400: oklch(74% 0.07 35);
--color-clay-500: oklch(70% 0.08 35);
--color-clay-600: oklch(62% 0.09 35);
--color-clay-700: oklch(50% 0.07 35);
--color-clay-800: oklch(38% 0.05 35);
--color-clay-900: oklch(28% 0.035 35);
--color-sandstone-50: oklch(98% 0.003 85);
--color-sandstone-100: oklch(96% 0.006 85);
--color-sandstone-200: oklch(92% 0.012 85);

View File

@ -1,25 +1,29 @@
<template>
<section class="w-full">
<!-- two-column card ----------------------------->
<div class="flex flex-col md:flex-row">
<div class="flex flex-col md:flex-row gap-4">
<!-- left half : image carousel -->
<AppThumbnailCarousel class="basis-1/3"></AppThumbnailCarousel>
<AppThumbnailCarousel :images="extract(apartment.images)" class="basis-1/3"></AppThumbnailCarousel>
<!-- right half : text (top) & icons (bottom) -->
<div class="basis-2/3 flex flex-col h-full p-6">
<div class="basis-2/3 flex flex-col h-full p-4 gap-4">
<!-- title + intro copy -->
<div class="flex flex-col gap-4">
<div>
<h2 class="text-3xl font-bold text-neutral-900 mb-2">
{{ rt(apartment.title) }}
</h2>
<p class="text-neutral-700">
{{
rt(apartment.subtitle)
}}
</p>
</div>
<div class="flex-1 flex flex-col gap-4">
<!-- title -->
<h2 class="text-2xl font-bold">
{{ rt(apartment.title) }}
</h2>
<!-- description (optional) -->
<p
v-if="true"
class="text-lg pb-4">
{{
rt(apartment.subtitle)
}}
</p>
<!-- anything the parent puts in here -->
<AppFeaturesGrid :features="apartment.features"/>
</div>
<div class="flex-1"></div>
@ -56,4 +60,8 @@ const {t, tm, rt} = useVariantData()
defineProps<{ apartment: Apartment }>()
const { variantPath } = useVariantPath()
function extract(o) {
return o.map(i => rt(i))
}
</script>

View File

@ -13,22 +13,22 @@
:title="t('footer.questions')"
:description="t('footer.prompt')">
<!-- Contact shortcuts -->
<div class="mt-2 space-y-1 text-sm">
<div class="mt-2 space-y-1">
<!-- Phone (phone + WhatsApp icons) -->
<div class="flex items-center gap-2">
<!-- Heroicons phone -->
<UIcon name="i-heroicons-phone" class="w-4 h-4 text-primary-600"/>
<UIcon name="i-heroicons-phone" class="w-4 h-4 text-sm text-neutral-600"/>
<!-- WhatsApp icon (any Iconify set you use) -->
<UIcon name="i-uil-whatsapp" class="w-4 h-4 text-primary-600"/>
<a :href="`tel:${t('contact.phone').replace(/\s+/g, '')}`" class="hover:underline">
<UIcon name="i-uil-whatsapp" class="w-4 h-4 text-sm text-neutral-600"/>
<a :href="`tel:${t('contact.phone').replace(/\s+/g, '')}`" class="hover:underline text-sm text-neutral-600">
{{ t('contact.phone') }}
</a>
</div>
<!-- E-mail -->
<div class="flex items-center gap-2">
<UIcon name="i-heroicons-envelope" class="w-4 h-4 text-primary-600"/>
<a :href="`mailto:${t('contact.email')}`" class="hover:underline">
<UIcon name="i-heroicons-envelope" class="w-4 h-4 text-sm text-neutral-600"/>
<a :href="`mailto:${t('contact.email')}`" class="hover:underline text-sm text-neutral-600">
{{ t('contact.email') }}
</a>
</div>

View File

@ -1,5 +1,5 @@
<template>
<header class="sticky top-0 z-50 bg-neutral-200 shadow">
<header class="sticky top-0 z-50 bg-neutral-200 shadow text-neutral-700">
<AppStripe>
<nav class="mx-auto py-4 flex items-center justify-between">
<!-- your logo / home link -->

View File

@ -46,7 +46,7 @@ const wrapperFlex = computed(() =>
/>
<div>
<p class="font-semibold text-neutral-900">{{ title }}</p>
<p class="font-semibold text-neutral-700">{{ title }}</p>
<p class="text-sm text-neutral-600">{{ description }}</p>
<slot/>

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import {ref} from 'vue'
const {t, tm, rt} = useVariantData()
import {useTemplateRef} from '#imports'
interface Props {
@ -8,8 +9,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
images: () => [
'/ap-1/1.webp',
'/ap-1/2.webp'
'https://picsum.photos/200/300'
]
})
@ -25,39 +25,49 @@ function select(index: number) {
activeIndex.value = index
carousel.value?.emblaApi?.scrollTo(index)
}
</script>
<template>
<div class="flex flex-col items-center justify-center p-4">
<div class="flex flex-col items-center justify-start p-4">
<!-- main slider -->
<UCarousel
ref="carousel"
auto-height
arrows
prev-icon="i-lucide-chevron-left"
next-icon="i-lucide-chevron-right"
v-slot="{ item }"
:items="props.images"
:autoplay="{ delay: 5_000 }"
:ui="{item: 'ps-0'}"
:ui="{item: 'ps-0',
controls: 'absolute top-6 inset-x-15',
dots: '-top-7',
dot: 'w-1 h-1'}"
loop
class="relative w-full max-w-3xl"
class="relative w-full"
@select="onSelect"
>
<img :src="item" class="w-full h-auto object-cover" alt="Bild"/>
<img :src="item" class="object-cover" alt="Bild"/>
</UCarousel>
<!-- thumbnails -->
<div class="flex gap-2 pt-4">
<button
v-for="(thumb, idx) in props.images"
:key="idx"
@click="select(idx)"
:class="[
'size-16 rounded-lg overflow-hidden opacity-40 hover:opacity-100',
'transition-opacity focus:outline-none focus-visible:ring',
activeIndex === idx && 'opacity-100 ring-2 ring-primary-500'
]"
>
<img :src="thumb" alt="" class="w-full h-full object-cover"/>
</button>
</div>
<!-- <div class="flex flex-wrap gap-2 pt-4">-->
<!-- <button-->
<!-- v-for="(thumb, idx) in props.images"-->
<!-- :key="idx"-->
<!-- @click="select(idx)"-->
<!-- :class="[-->
<!-- 'size-16 rounded-lg overflow-hidden opacity-40 hover:opacity-100',-->
<!-- 'transition-opacity focus:outline-none focus-visible:ring',-->
<!-- activeIndex === idx && 'opacity-100 ring-2 ring-primary-500'-->
<!-- ]"-->
<!-- >-->
<!-- <img :src="thumb" alt="" class="w-full h-full object-cover"/>-->
<!-- </button>-->
<!-- </div>-->
</div>
</template>

View File

@ -9,7 +9,7 @@
<!-- description (optional) -->
<p
v-if="true"
class="text-gray-700 text-lg pb-4">
class="text-lg pb-4">
{{ t(`${i18nKey}.description`) }}
</p>

View File

@ -134,7 +134,9 @@
},
"features": {
"title": "Darauf können Sie sich freuen!",
"description": "Was Sie in Ihrem Urlaub im Landhaus Appartement Panoramablick erwartet."
"description-1": "Was Sie in Ihrem Urlaub im Landhaus Appartement Panoramablick erwartet. Unser Tipp:",
"description-2": "Reservieren",
"description-3": "Sie online beim Sportgeschäft in nächster Nähe und sichern sich dabei exklusive Rabatte."
}
},
"apartments": {
@ -155,14 +157,23 @@
"#su": "Blabla",
"#wi": "Miau"
},
"thumbnail": "/ap-1/1.webp",
"thumbnail": "/apartments/2/4.jpg",
"highlights": [
"Ab € 225,- pro Tag",
"2 bis 4 Personen"
],
"images": [
"/apartments/1/1.webp",
"/apartments/1/2.webp"
"/apartments/1/1.jpg",
"/apartments/1/2.jpg",
"/apartments/1/3.jpg",
"/apartments/1/4.jpg",
"/apartments/1/5.jpg",
"/apartments/1/6.jpg",
"/apartments/1/7.jpg",
"/apartments/1/8.jpg",
"/apartments/1/9.jpg",
"/apartments/1/10.jpg",
"/apartments/1/11.jpg"
],
"features": [
{
@ -219,14 +230,25 @@
"id": "apartment-2",
"title": "Apartment 2",
"subtitle": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"thumbnail": "/ap-1/1.webp",
"thumbnail": "/apartments/2/8.jpg",
"highlights": [
"Ab € 225,- pro Tag",
"4 bis 6 Personen"
],
"images": [
"/ap-2/1.webp",
"/ap-2/2.webp"
"/apartments/2/1.jpg",
"/apartments/2/2.jpg",
"/apartments/2/3.jpg",
"/apartments/2/4.jpg",
"/apartments/2/5.jpg",
"/apartments/2/6.jpg",
"/apartments/2/7.jpg",
"/apartments/2/8.jpg",
"/apartments/2/9.jpg",
"/apartments/2/10.jpg",
"/apartments/2/11.jpg",
"/apartments/2/12.jpg",
"/apartments/2/13.jpg"
],
"features": [
{
@ -283,14 +305,26 @@
"id": "apartment-3",
"title": "Apartment 3",
"subtitle": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"thumbnail": "/ap-1/1.webp",
"thumbnail": "/apartments/2/7.jpg",
"highlights": [
"Ab € 225,- pro Tag",
"4 bis 6 Personen"
],
"images": [
"/ap-2/1.webp",
"/ap-2/2.webp"
"/apartments/3/1.jpg",
"/apartments/3/2.jpg",
"/apartments/3/3.jpg",
"/apartments/3/4.jpg",
"/apartments/3/5.jpg",
"/apartments/3/6.jpg",
"/apartments/3/7.jpg",
"/apartments/3/8.jpg",
"/apartments/3/9.jpg",
"/apartments/3/10.jpg",
"/apartments/3/11.jpg",
"/apartments/3/12.jpg",
"/apartments/3/13.jpg",
"/apartments/3/14.jpg"
],
"features": [
{

View File

@ -29,8 +29,8 @@ export default defineNuxtConfig({
websites: {
defaultVariant: 'su',
variants: [
{ code: 'su', name: 'Summer' },
{ code: 'wi', name: 'Winter' }
{ code: 'su', name: 'Summer', icon: 'i-lucide-sun' },
{ code: 'wi', name: 'Winter', icon: 'i-lucide-snowflake'}
]
}
})

View File

@ -24,15 +24,19 @@ useHead({
<template>
<AppFlatSection>
<div class="flex flex-col items-center justify-center">
<span class="text-lg text-neutral-600">Coming Soon</span>
</div>
<!-- SSR-safe: iframe appears only in the browser -->
<ClientOnly>
<iframe
id="iframeCapCorn"
src="https://www.capcorn.net/MasterReq?MB=1487&FL=17&LG=0"
frameborder="0"
width="100%"
scrolling="auto"
/>
</ClientOnly>
<!-- <ClientOnly>-->
<!-- <iframe-->
<!-- id="iframeCapCorn"-->
<!-- src="https://www.capcorn.net/MasterReq?MB=1487&FL=17&LG=0"-->
<!-- frameborder="0"-->
<!-- width="100%"-->
<!-- scrolling="auto"-->
<!-- />-->
<!-- </ClientOnly>-->
</AppFlatSection>
</template>

View File

@ -8,7 +8,7 @@
<h1 class="text-3xl sm:text-4xl font-bold mb-4">{{ t('contact.title') }}</h1>
<!-- Extended intro -->
<p class="text-neutral-700 mb-4 max-w-prose">
<p class="text-neutral-600 mb-4 max-w-prose">
{{ t('contact.description') }}
</p>
@ -17,24 +17,24 @@
<!-- Phone (phone + WhatsApp icons) -->
<div class="flex items-center gap-2">
<!-- Heroicons phone -->
<UIcon name="i-heroicons-phone" class="w-4 h-4 text-primary-600"/>
<UIcon name="i-heroicons-phone" class="w-4 h-4 text-neutral-600"/>
<!-- WhatsApp icon (any Iconify set you use) -->
<UIcon name="i-uil-whatsapp" class="w-4 h-4 text-primary-600"/>
<a :href="`tel:${t('contact.phone').replace(/\s+/g, '')}`" class="hover:underline">
<UIcon name="i-uil-whatsapp" class="w-4 h-4 text-neutral-600"/>
<a :href="`tel:${t('contact.phone').replace(/\s+/g, '')}`" class="hover:underline text-neutral-600">
{{ t('contact.phone') }}
</a>
</div>
<!-- E-mail -->
<div class="flex items-center gap-2">
<UIcon name="i-heroicons-envelope" class="w-4 h-4 text-primary-600"/>
<a :href="`mailto:${t('contact.email')}`" class="hover:underline">
<UIcon name="i-heroicons-envelope" class="w-4 h-4 text-neutral-600"/>
<a :href="`mailto:${t('contact.email')}`" class="hover:underline text-neutral-600">
{{ t('contact.email') }}
</a>
</div>
</div>
<p class="text-neutral-700 mb-4 max-w-prose">
<p class="text-neutral-600 mb-4 max-w-prose">
{{ t('contact.online-1') }}
<UButton :to="variantPath('book')" variant="outline" trailing-icon="i-heroicons-arrow-right">{{
t('contact.online-2')

View File

@ -8,7 +8,7 @@
:autoplay="{ delay: 5000 }"
:loop="true"
:fade="true"
:duration="75"
:duration="150"
class="w-full h-full custom-carousel">
<div class="w-full h-full flex items-center justify-center">
<img :src="item" class="w-full h-full object-cover" alt="Demo Picture"/>
@ -132,9 +132,22 @@
</AppFlatSection>
<AppCardSection>
<AppTitleText i18n-key="landing.features" class="pr">
<div class="flex-1 pr">
<h2 class="text-2xl font-bold mb-4">
{{ t('landing.features.title') }}
</h2>
<p class="text-lg pb-4">
{{ t('landing.features.description-1') }}
<UButton to="https://www.bruendl.at/" variant="outline" trailing-icon="i-heroicons-arrow-right">{{
t('landing.features.description-2')
}}
</UButton>
{{ t('landing.features.description-3') }}
</p>
<AppFeaturesGrid :features="tm('features')"/>
</AppTitleText>
</div>
</AppCardSection>
</template>
@ -147,8 +160,10 @@
<script setup lang="ts">
const images = [
'/house.webp',
'/panorama.webp'
'/landing/2.jpg',
'/landing/1.jpg',
'/landing/3.jpg',
'/landing/4.jpg'
]
const {t, tm, rt} = useVariantData()

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -12,7 +12,8 @@ const availableVariants = computed(() =>
v-for="variant in availableVariants"
:key="variant.code"
:to="switchVariantPath(variant.code)"
class="flex items-center justify-center"
>
{{ variant.code.toUpperCase() }}
<UIcon :name="variant.icon"/>
</NuxtLink>
</template>