ghostguild-org/app/components/OnboardingWidget.vue
Jennie Robinson Faber d6cdf45838 style(visual-fidelity): components — batches B,E,G,H
- B: token-equivalent rgba → color-mix in SignupFlowOverlay, OnboardingWidget
- E: drop text-white Tailwind utility from ImageUpload remove-button (now color: var(--parch-text) inline)
- G: typography off-scale snaps (9→10, 14→13, 15→16, 19→18 px)
- H: padding off-scale snaps in BoardPostCard/Form, CirclePicker, FilterBar, LoginModal
2026-04-30 00:13:13 +01:00

176 lines
4.6 KiB
Vue

<template>
<ClientOnly>
<div v-if="!loading" class="onboarding-widget">
<!-- Welcome mode: onboarding in progress -->
<template v-if="!isComplete">
<div class="ow-prompt">&gt; welcome</div>
<div class="ow-message">You are in the <strong>Ghost Guild</strong>. A few passages remain unexplored.</div>
<div class="ow-hint">Next: {{ currentSuggestion.text }}</div>
<NuxtLink
v-if="currentSuggestion.action && !currentSuggestion.isExternal"
:to="currentSuggestion.action"
class="ow-action"
>
{{ currentSuggestion.actionText }} &rarr;
</NuxtLink>
<a
v-else-if="currentSuggestion.isExternal"
:href="currentSuggestion.action"
target="_blank"
rel="noopener"
class="ow-action"
@click="trackGoal('wikiClicked')"
>
{{ currentSuggestion.actionText }} &rarr;
</a>
<div class="ow-progress">
<span class="ow-bar"><span class="ow-bar-fill">{{ barFill }}</span><span class="ow-bar-empty">{{ barEmpty }}</span></span>
{{ completedCount }} of 4 explored
<button
v-if="currentSuggestion.key"
type="button"
class="ow-skip"
@click="handleSkip"
>Skip this</button>
</div>
</template>
<!-- Suggestion mode: onboarding complete -->
<template v-else>
<!-- Empty state -->
<div v-if="currentSuggestion.key === 'empty'" class="ow-prompt">&gt; look</div>
<div v-if="currentSuggestion.key === 'empty'" class="ow-message ow-message--dim">{{ currentSuggestion.text }}</div>
<!-- Recommendation (event, board, or wiki) -->
<template v-if="currentSuggestion.key !== 'empty'">
<div class="ow-prompt">&gt; look</div>
<div class="ow-message">{{ currentSuggestion.text }}</div>
<a
v-if="currentSuggestion.isExternal && currentSuggestion.action"
:href="currentSuggestion.action"
target="_blank"
rel="noopener"
class="ow-action"
>
{{ currentSuggestion.actionText }} &rarr;
</a>
<NuxtLink
v-else-if="currentSuggestion.action"
:to="currentSuggestion.action"
class="ow-action"
>
{{ currentSuggestion.actionText }} &rarr;
</NuxtLink>
</template>
</template>
</div>
</ClientOnly>
</template>
<script setup>
const { goals, isComplete, currentSuggestion, trackGoal, skipSuggestion, loading } = useOnboarding()
const handleSkip = () => {
const key = currentSuggestion.value?.key
if (key) skipSuggestion(key)
}
const completedCount = computed(() => {
const g = goals.value
return [g.hasProfileTags, g.hasVisitedEvent, g.hasEngagedBoard, g.hasClickedWiki]
.filter(Boolean).length
})
const barFill = computed(() => '[' + '#'.repeat(completedCount.value * 2))
const barEmpty = computed(() => '-'.repeat((4 - completedCount.value) * 2) + ']')
</script>
<style scoped>
.onboarding-widget {
padding: 16px 20px;
border-bottom: 1px dashed var(--parch-border);
background: var(--parch);
color: var(--parch-text);
font-size: 12px;
line-height: 1.7;
}
.ow-prompt {
color: var(--parch-accent);
margin-bottom: 6px;
}
.ow-message {
color: var(--parch-text);
margin-bottom: 2px;
}
.ow-message--dim {
color: var(--parch-text-dim);
}
.ow-hint {
color: var(--parch-text-dim);
font-size: 11px;
}
.ow-action {
display: inline-block;
margin-top: 8px;
padding: 4px 12px;
border: 1px dashed color-mix(in srgb, var(--parch-text) 25%, transparent);
color: var(--parch-accent);
font-size: 11px;
text-decoration: none;
letter-spacing: 0.04em;
}
.ow-action:hover {
border-color: var(--parch-accent);
border-style: solid;
text-decoration: none;
}
.ow-progress {
margin-top: 10px;
padding-top: 8px;
border-top: 1px dashed color-mix(in srgb, var(--parch-text) 12%, transparent);
font-size: 11px;
color: var(--parch-text-dim);
display: flex;
align-items: center;
gap: 6px;
}
.ow-bar {
display: inline-flex;
gap: 0;
letter-spacing: 0;
}
.ow-bar-fill {
color: var(--parch-accent);
}
.ow-bar-empty {
color: color-mix(in srgb, var(--parch-text) 20%, transparent);
}
.ow-skip {
margin-left: auto;
background: none;
border: none;
color: var(--parch-text-dim);
font-family: inherit;
font-size: 11px;
cursor: pointer;
padding: 0;
text-decoration: underline;
text-decoration-style: dashed;
text-underline-offset: 2px;
}
.ow-skip:hover {
color: var(--parch-accent);
}
</style>