From 5cdbb76ab7f2f9a410ddf09f18e0c6613263a749 Mon Sep 17 00:00:00 2001 From: Patrick Mulligan Date: Sat, 1 Aug 2026 00:01:28 +0200 Subject: [PATCH] =?UTF-8?q?feat(deck):=20mobile=20support=20=E2=80=94=20sc?= =?UTF-8?q?rollable=20slides,=20swipe,=20desktop=20notice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-heavy slides were clipped on phones (fixed h-dvh + centered overflow-hidden collided with the top/bottom bars). SlideFrame now scrolls and top-aligns below md (clearing the header/controls), while desktop keeps the centered, full-bleed look unchanged. Add horizontal touch-swipe navigation (vertical scroll still works). Add a shadcn-vue Alert component and show a mobile-only "Best viewed on desktop" notice on the home slide; keyboard hint is desktop-only. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/ui/alert/Alert.vue | 16 ++++++++++++++ src/components/ui/alert/AlertDescription.vue | 12 ++++++++++ src/components/ui/alert/AlertTitle.vue | 12 ++++++++++ src/components/ui/alert/index.ts | 23 ++++++++++++++++++++ src/deck/Deck.vue | 18 ++++++++++++++- src/deck/components/SlideFrame.vue | 4 ++-- src/deck/slides/TitleSlide.vue | 13 ++++++++++- 7 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/components/ui/alert/Alert.vue create mode 100644 src/components/ui/alert/AlertDescription.vue create mode 100644 src/components/ui/alert/AlertTitle.vue create mode 100644 src/components/ui/alert/index.ts diff --git a/src/components/ui/alert/Alert.vue b/src/components/ui/alert/Alert.vue new file mode 100644 index 0000000..7c74e2a --- /dev/null +++ b/src/components/ui/alert/Alert.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/components/ui/alert/AlertDescription.vue b/src/components/ui/alert/AlertDescription.vue new file mode 100644 index 0000000..40e116d --- /dev/null +++ b/src/components/ui/alert/AlertDescription.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/components/ui/alert/AlertTitle.vue b/src/components/ui/alert/AlertTitle.vue new file mode 100644 index 0000000..7f2fd79 --- /dev/null +++ b/src/components/ui/alert/AlertTitle.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/components/ui/alert/index.ts b/src/components/ui/alert/index.ts new file mode 100644 index 0000000..08590d7 --- /dev/null +++ b/src/components/ui/alert/index.ts @@ -0,0 +1,23 @@ +import { cva, type VariantProps } from 'class-variance-authority' + +export { default as Alert } from './Alert.vue' +export { default as AlertTitle } from './AlertTitle.vue' +export { default as AlertDescription } from './AlertDescription.vue' + +export const alertVariants = cva( + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', + { + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +export type AlertVariants = VariantProps diff --git a/src/deck/Deck.vue b/src/deck/Deck.vue index 2ed6237..c21be4e 100644 --- a/src/deck/Deck.vue +++ b/src/deck/Deck.vue @@ -49,6 +49,22 @@ const slides = [ const { index, direction, goto, next, prev, isFirst, isLast } = useDeck(slides.length) const current = computed(() => slides[index.value]) const transitionName = computed(() => (direction.value === 1 ? 'slide-next' : 'slide-prev')) + +// swipe navigation (touch) โ€” horizontal only, so vertical scrolling still works +let touchX = 0 +let touchY = 0 +function onTouchStart(e: TouchEvent) { + touchX = e.changedTouches[0].clientX + touchY = e.changedTouches[0].clientY +} +function onTouchEnd(e: TouchEvent) { + const dx = e.changedTouches[0].clientX - touchX + const dy = e.changedTouches[0].clientY - touchY + if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) { + if (dx < 0) next() + else prev() + } +}