feat(ui): install shadcn Card; sweep card patterns site-wide
Add the shadcn-vue Card primitive (Card / CardHeader / CardTitle / CardDescription / CardContent / CardFooter) at src/components/ui/card. Replace ~20 hand-rolled "rounded-lg border border-border bg-card …" patterns across nine views with <Card>: - ConceptView (slow-farming pillars) - VisionValuesView (philosophy + pillars + team) - GalleryView (image figure cards) - EventsView (program cards) - SymposiumView (included items + apply steps) - LongStaysView (path cards) - OpportunitiesView (group cards + apply explainers) - AccommodationView (rooms + cabins + exterior items) - ReservationsView (kind cards + contact card) - MarketplaceView (category cards) - HomeView (featured events) For image-bearing cards (events / rooms), use Card + CardContent so the image stays flush at the top of the card and the inner padding lives on the content slot. For clickable cards, the RouterLink wraps the Card so the whole card is the link target. Variants where the card sits on a tinted section (philosophy items on bg-card section, cabins on bg-card section) override Card's default bg-card with bg-background via the class prop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4fcbc22d1c
commit
4cb0fa14a2
18 changed files with 265 additions and 197 deletions
21
src/components/ui/card/Card.vue
Normal file
21
src/components/ui/card/Card.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'rounded-lg border bg-card text-card-foreground shadow-sm',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardContent.vue
Normal file
14
src/components/ui/card/CardContent.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('p-6 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardDescription.vue
Normal file
14
src/components/ui/card/CardDescription.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p :class="cn('text-sm text-muted-foreground', props.class)">
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
14
src/components/ui/card/CardFooter.vue
Normal file
14
src/components/ui/card/CardFooter.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex items-center p-6 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardHeader.vue
Normal file
14
src/components/ui/card/CardHeader.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col gap-y-1.5 p-6', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
18
src/components/ui/card/CardTitle.vue
Normal file
18
src/components/ui/card/CardTitle.vue
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h3
|
||||
:class="
|
||||
cn('text-2xl font-semibold leading-none tracking-tight', props.class)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</h3>
|
||||
</template>
|
||||
6
src/components/ui/card/index.ts
Normal file
6
src/components/ui/card/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export { default as Card } from "./Card.vue"
|
||||
export { default as CardContent } from "./CardContent.vue"
|
||||
export { default as CardDescription } from "./CardDescription.vue"
|
||||
export { default as CardFooter } from "./CardFooter.vue"
|
||||
export { default as CardHeader } from "./CardHeader.vue"
|
||||
export { default as CardTitle } from "./CardTitle.vue"
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
|
||||
const { t, tm, rt } = useI18n()
|
||||
|
||||
|
|
@ -82,18 +83,15 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
|
|||
<p class="mt-3 text-muted-foreground">{{ t('accommodation.rooms.subtitle') }}</p>
|
||||
</div>
|
||||
<ul class="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<li
|
||||
v-for="room in rooms"
|
||||
:key="room.key"
|
||||
class="overflow-hidden rounded-lg border border-border bg-card"
|
||||
>
|
||||
<li v-for="room in rooms" :key="room.key">
|
||||
<Card class="overflow-hidden">
|
||||
<img
|
||||
:src="room.image"
|
||||
alt=""
|
||||
class="aspect-[4/3] w-full object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="p-5">
|
||||
<CardContent class="p-5 pt-5">
|
||||
<div class="flex items-baseline justify-between gap-3">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t(`accommodation.rooms.${room.key}.name`) }}
|
||||
|
|
@ -116,7 +114,8 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
|
|||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`accommodation.rooms.${room.key}.summary`) }}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
|
@ -131,11 +130,8 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
|
|||
<p class="mt-3 text-muted-foreground">{{ t('accommodation.cabins.subtitle') }}</p>
|
||||
</div>
|
||||
<ul class="mt-8 grid gap-6 md:grid-cols-3">
|
||||
<li
|
||||
v-for="cabin in cabins"
|
||||
:key="cabin.key"
|
||||
class="overflow-hidden rounded-lg border border-border bg-background"
|
||||
>
|
||||
<li v-for="cabin in cabins" :key="cabin.key">
|
||||
<Card class="overflow-hidden bg-background">
|
||||
<img
|
||||
:src="cabin.image"
|
||||
alt=""
|
||||
|
|
@ -152,6 +148,7 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
|
|||
{{ t('accommodation.statusComingSoon') }}
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -166,12 +163,10 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
|
|||
<p class="mt-3 text-muted-foreground">{{ t('accommodation.exterior.subtitle') }}</p>
|
||||
</div>
|
||||
<ul class="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<li
|
||||
v-for="(item, i) in exteriorItems"
|
||||
:key="i"
|
||||
class="rounded-lg border border-dashed border-border bg-secondary/20 p-5 text-sm text-foreground/85"
|
||||
>
|
||||
<li v-for="(item, i) in exteriorItems" :key="i">
|
||||
<Card class="border-dashed bg-secondary/20 p-5 text-sm text-foreground/85">
|
||||
{{ rt(item) }}
|
||||
</Card>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -96,18 +97,14 @@ const pillars = [
|
|||
<p class="mt-3 text-muted-foreground">{{ t('concept.slowFarming.body') }}</p>
|
||||
</div>
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-3">
|
||||
<article
|
||||
v-for="p in pillars"
|
||||
:key="p.key"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="p in pillars" :key="p.key" class="p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t(`concept.slowFarming.${p.key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`concept.slowFarming.${p.key}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -64,15 +65,16 @@ const events = [
|
|||
<component
|
||||
:is="e.to ? 'RouterLink' : 'article'"
|
||||
v-bind="e.to ? { to: e.to } : {}"
|
||||
class="group block h-full overflow-hidden rounded-lg border border-border bg-card transition hover:shadow-md"
|
||||
class="group block h-full"
|
||||
>
|
||||
<Card class="h-full overflow-hidden transition hover:shadow-md">
|
||||
<img
|
||||
:src="e.image"
|
||||
alt=""
|
||||
class="aspect-[4/3] w-full object-cover transition group-hover:scale-[1.02]"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="p-5">
|
||||
<CardContent class="p-5 pt-5">
|
||||
<p class="text-xs uppercase tracking-wider text-accent">
|
||||
{{ t(`events.${e.key}.date`) }}
|
||||
</p>
|
||||
|
|
@ -85,7 +87,8 @@ const events = [
|
|||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`events.${e.key}.description`) }}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</component>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -47,11 +48,8 @@ const items = [
|
|||
|
||||
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
|
||||
<ul class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<li
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
class="group overflow-hidden rounded-lg border border-border bg-card"
|
||||
>
|
||||
<li v-for="item in items" :key="item.key" class="group">
|
||||
<Card class="overflow-hidden">
|
||||
<figure>
|
||||
<img
|
||||
:src="item.src"
|
||||
|
|
@ -65,6 +63,7 @@ const items = [
|
|||
{{ t(`gallery.captions.${item.key}`) }}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</Card>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import cosmicStag from '@/assets/cosmic-stag.avif'
|
||||
import heroLandscape from '@/assets/hero-landscape.webp'
|
||||
import sectionTile from '@/assets/section-tile.webp'
|
||||
|
|
@ -192,15 +193,16 @@ const featuredEvents = [
|
|||
v-for="e in featuredEvents"
|
||||
:key="e.key"
|
||||
:to="e.to"
|
||||
class="group overflow-hidden rounded-lg border border-border bg-card transition hover:shadow-md"
|
||||
class="group block"
|
||||
>
|
||||
<Card class="overflow-hidden transition hover:shadow-md">
|
||||
<img
|
||||
:src="e.image"
|
||||
alt=""
|
||||
class="aspect-[4/3] w-full object-cover transition group-hover:scale-[1.02]"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="p-5">
|
||||
<CardContent class="p-5 pt-5">
|
||||
<p class="text-xs uppercase tracking-wider text-accent">
|
||||
{{ t(`events.${e.key}.date`) }}
|
||||
</p>
|
||||
|
|
@ -210,7 +212,8 @@ const featuredEvents = [
|
|||
<p class="mt-1 text-xs text-foreground/70">
|
||||
{{ t(`events.${e.key}.location`) }}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="mt-10 text-center">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -31,18 +32,14 @@ const paths = ['exchange', 'rental', 'partial', 'funded'] as const
|
|||
{{ t('longStays.pathsTitle') }}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-2">
|
||||
<article
|
||||
v-for="key in paths"
|
||||
:key="key"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="key in paths" :key="key" class="p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t(`longStays.paths.${key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-base leading-relaxed text-foreground/85">
|
||||
{{ t(`longStays.paths.${key}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -28,18 +29,14 @@ const categories = ['fresh', 'pantry', 'craft'] as const
|
|||
{{ t('marketplace.categoriesTitle') }}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-3">
|
||||
<article
|
||||
v-for="key in categories"
|
||||
:key="key"
|
||||
class="rounded-lg border border-dashed border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="key in categories" :key="key" class="border-dashed p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t(`marketplace.categories.${key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-base leading-relaxed text-foreground/85">
|
||||
{{ t(`marketplace.categories.${key}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -43,17 +44,15 @@ const applyKeys = ['model', 'window', 'open'] as const
|
|||
<p class="mt-3 text-muted-foreground">{{ t('opportunities.groupsSubtitle') }}</p>
|
||||
</div>
|
||||
<ul class="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<li
|
||||
v-for="key in groups"
|
||||
:key="key"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<li v-for="key in groups" :key="key">
|
||||
<Card class="p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t(`opportunities.groups.${key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`opportunities.groups.${key}Positions`) }}
|
||||
</p>
|
||||
</Card>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
|
@ -65,18 +64,14 @@ const applyKeys = ['model', 'window', 'open'] as const
|
|||
{{ t('opportunities.applyTitle') }}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-3">
|
||||
<article
|
||||
v-for="key in applyKeys"
|
||||
:key="key"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="key in applyKeys" :key="key" class="p-6">
|
||||
<h3 class="font-serif text-lg font-semibold">
|
||||
{{ t(`opportunities.apply.${key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`opportunities.apply.${key}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="mt-10 flex flex-wrap items-center gap-3">
|
||||
<Button as-child>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -31,18 +32,14 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
|
|||
{{ t('reservations.kindsTitle') }}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<article
|
||||
v-for="key in kinds"
|
||||
:key="key"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="key in kinds" :key="key" class="p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t(`reservations.kinds.${key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`reservations.kinds.${key}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -87,7 +84,7 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="rounded-lg border border-border bg-card p-6 lg:col-span-2">
|
||||
<Card class="p-6 lg:col-span-2">
|
||||
<h3 class="font-serif text-xl font-semibold">
|
||||
{{ t('reservations.contactCard.title') }}
|
||||
</h3>
|
||||
|
|
@ -118,7 +115,7 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
|
|||
<dd class="mt-1">{{ t('reservations.contactCard.openingValue') }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</aside>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -56,18 +57,14 @@ const applySteps = ['stepOne', 'stepTwo', 'stepThree'] as const
|
|||
{{ t('symposium.includedTitle') }}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<article
|
||||
v-for="key in included"
|
||||
:key="key"
|
||||
class="rounded-lg border border-border bg-background p-6"
|
||||
>
|
||||
<Card v-for="key in included" :key="key" class="bg-background p-6">
|
||||
<h3 class="font-serif text-lg font-semibold">
|
||||
{{ t(`symposium.included.${key}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`symposium.included.${key}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -89,17 +86,15 @@ const applySteps = ['stepOne', 'stepTwo', 'stepThree'] as const
|
|||
{{ t('symposium.applyTitle') }}
|
||||
</h2>
|
||||
<ol class="mt-8 grid gap-6 md:grid-cols-3">
|
||||
<li
|
||||
v-for="step in applySteps"
|
||||
:key="step"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<li v-for="step in applySteps" :key="step">
|
||||
<Card class="p-6">
|
||||
<h3 class="font-serif text-lg font-semibold">
|
||||
{{ t(`symposium.apply.${step}Title`) }}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`symposium.apply.${step}Body`) }}
|
||||
</p>
|
||||
</Card>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="mt-10 flex flex-wrap items-center gap-3">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Card } from '@/components/ui/card'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -55,11 +56,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
|
|||
<p class="mt-3 text-muted-foreground">{{ t('vision.philosophySubtitle') }}</p>
|
||||
</div>
|
||||
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-5">
|
||||
<article
|
||||
v-for="(p, i) in philosophy"
|
||||
:key="p"
|
||||
class="rounded-lg border border-border bg-background p-5"
|
||||
>
|
||||
<Card v-for="(p, i) in philosophy" :key="p" class="bg-background p-5">
|
||||
<div class="font-serif text-2xl text-accent">{{ i + 1 }}</div>
|
||||
<h3 class="mt-2 font-serif text-lg font-semibold">
|
||||
{{ t(`vision.philosophy.${p}Title`) }}
|
||||
|
|
@ -67,7 +64,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
|
|||
<p class="mt-2 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`vision.philosophy.${p}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -81,16 +78,12 @@ const team = ['patrick', 'coco', 'charlie'] as const
|
|||
<p class="mt-3 text-muted-foreground">{{ t('vision.pillarsSubtitle') }}</p>
|
||||
</div>
|
||||
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<article
|
||||
v-for="p in pillars"
|
||||
:key="p"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="p in pillars" :key="p" class="p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">{{ t(`vision.pillars.${p}Title`) }}</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`vision.pillars.${p}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -101,11 +94,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
|
|||
{{ t('vision.teamTitle') }}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-3">
|
||||
<article
|
||||
v-for="m in team"
|
||||
:key="m"
|
||||
class="rounded-lg border border-border bg-card p-6"
|
||||
>
|
||||
<Card v-for="m in team" :key="m" class="p-6">
|
||||
<h3 class="font-serif text-xl font-semibold">{{ t(`vision.team.${m}Name`) }}</h3>
|
||||
<p class="mt-1 text-xs uppercase tracking-wider text-accent">
|
||||
{{ t(`vision.team.${m}Role`) }}
|
||||
|
|
@ -113,7 +102,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
|
|||
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
|
||||
{{ t(`vision.team.${m}Body`) }}
|
||||
</p>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue