fix(ui): disambiguate annual tier labels

"$50/yr" was ambiguous — could mean the $5 tier in annual mode or the
$50 tier in monthly mode. On /join the dropdown now shows both prices
("$5/mo → $50/yr") in annual mode. On the account page TierPicker
gains a subtitle slot; annual mode shows "$N/mo tier" beneath the
annual price so members recognize which tier they're on.
This commit is contained in:
Jennie Robinson Faber 2026-04-18 22:06:38 +01:00
parent 8ceaebb268
commit fd9ce5bc2c
3 changed files with 28 additions and 8 deletions

View file

@ -8,6 +8,7 @@
@click="$emit('update:modelValue', tier.amount)" @click="$emit('update:modelValue', tier.amount)"
> >
<span class="tier-amount">{{ tier.display }}</span> <span class="tier-amount">{{ tier.display }}</span>
<span v-if="tier.subtitle" class="tier-subtitle">{{ tier.subtitle }}</span>
</div> </div>
</div> </div>
</template> </template>
@ -78,6 +79,15 @@ defineEmits(["update:modelValue"]);
color: var(--candle); color: var(--candle);
} }
.tier-subtitle {
display: block;
margin-top: 4px;
font-size: 11px;
color: var(--text-dim);
font-family: "Commit Mono", monospace;
letter-spacing: 0.02em;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.tier-picker { .tier-picker {
flex-wrap: wrap; flex-wrap: wrap;

View file

@ -467,14 +467,19 @@ const paymentToken = ref(null);
const circleOptions = getCircleOptions(); const circleOptions = getCircleOptions();
// Minimal labels for the dropdown reactive to cadence. // Minimal labels for the dropdown reactive to cadence.
// In annual mode, show both monthly and annual price so $50/yr (the $5 tier annual)
// is visually distinct from $500/yr (the $50 tier annual).
const contributionItems = computed(() => { const contributionItems = computed(() => {
return Object.values(CONTRIBUTION_TIERS).map((tier) => { return Object.values(CONTRIBUTION_TIERS).map((tier) => {
const base = tier.amount; const base = tier.amount;
if (base === 0) return { value: tier.value, label: "$0" }; if (base === 0) return { value: tier.value, label: "$0" };
const amt = getTierAmount(tier, cadence.value); const monthlyLabel = `$${base}/mo`;
const suffix = cadence.value === "annual" ? "/yr" : "/mo"; const priceLabel =
const hint = tier.value === "15" && cadence.value !== "annual" ? " (suggested)" : ""; cadence.value === "annual"
return { value: tier.value, label: `$${amt}${suffix}${hint}` }; ? `${monthlyLabel}$${getTierAmount(tier, "annual")}/yr`
: monthlyLabel;
const hint = tier.value === "15" ? " (suggested)" : "";
return { value: tier.value, label: `${priceLabel}${hint}` };
}); });
}); });

View file

@ -239,10 +239,15 @@ const BASE_TIERS = [
const tiers = computed(() => { const tiers = computed(() => {
const cadence = memberData.value?.billingCadence || 'monthly'; const cadence = memberData.value?.billingCadence || 'monthly';
return BASE_TIERS.map((t) => ({ return BASE_TIERS.map((t) => {
...t, if (t.amount === 0) return { ...t, display: '$0' };
display: t.amount === 0 ? '$0' : `$${getTierAmount(t, cadence)}`, const suffix = cadence === 'annual' ? '/yr' : '/mo';
})); return {
...t,
display: `$${getTierAmount(t, cadence)}${suffix}`,
subtitle: cadence === 'annual' ? `$${t.amount}/mo tier` : null,
};
});
}); });
const currentContributionLabel = computed(() => { const currentContributionLabel = computed(() => {