refactor(app): migrate shared styles to tailwind

This commit is contained in:
2026-07-13 15:46:28 +08:00
parent 4682b4bf3f
commit 51462660a4
13 changed files with 145 additions and 289 deletions
@@ -4,6 +4,7 @@ import { describe, expect, it } from "vitest";
import { SubscriptionCtaButton } from "../subscription-cta-button";
import { SubscriptionPaymentMethod } from "../subscription-payment-method";
import { SubscriptionPaymentSuccessDialog } from "../subscription-payment-success-dialog";
import { StripePaymentDialog } from "../stripe-payment-dialog";
describe("subscription Tailwind components", () => {
it("renders SubscriptionCtaButton with loading state", () => {
@@ -68,4 +69,19 @@ describe("subscription Tailwind components", () => {
expect(html).toContain("-translate-y-0.5");
expect(html).toContain("/images/subscription/gcash-logo.svg");
});
it("renders StripePaymentDialog fallback with Tailwind dialog utilities", () => {
const html = renderToStaticMarkup(
<StripePaymentDialog
clientSecret="client-secret"
onClose={() => undefined}
/>,
);
expect(html).toContain('role="alertdialog"');
expect(html).toContain("fixed inset-0");
expect(html).toContain("max-w-(--dialog-wide-max-width,420px)");
expect(html).toContain("Payment unavailable");
expect(html).toContain("bg-[linear-gradient(var(--color-button-gradient-start,#ff67e0)");
});
});
@@ -1,98 +0,0 @@
.overlay {
position: fixed;
inset: 0;
z-index: 70;
display: flex;
align-items: center;
justify-content: center;
padding:
calc(var(--dialog-safe-margin, 20px) + var(--app-safe-top, 0px))
calc(var(--dialog-safe-margin, 20px) + var(--app-safe-right, 0px))
calc(var(--dialog-safe-margin, 20px) + var(--app-safe-bottom, 0px))
calc(var(--dialog-safe-margin, 20px) + var(--app-safe-left, 0px));
background: rgba(0, 0, 0, 0.5);
}
.dialog {
width: 100%;
max-width: var(--dialog-wide-max-width, 420px);
max-height: calc(
var(--app-visible-height, 100dvh) -
calc(var(--dialog-safe-margin, 20px) * 2)
);
overflow-y: auto;
border-radius: var(--responsive-card-radius, 32px);
background: var(--color-page-background, #ffffff);
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.16);
padding:
var(--responsive-card-padding-lg, 24px)
var(--responsive-card-padding, 18px)
var(--responsive-card-padding, 18px);
}
.header {
margin-bottom: var(--page-section-gap, 18px);
text-align: left;
}
.title {
margin: 0 0 clamp(7px, 1.481vw, 8px);
color: var(--color-text-foreground, #171717);
font-size: var(--responsive-page-title, 22px);
font-weight: 700;
line-height: 1.2;
}
.content {
margin: 0;
color: #393939;
font-size: var(--responsive-body, 14px);
line-height: 1.5;
}
.form {
display: flex;
flex-direction: column;
gap: var(--page-section-gap, 18px);
}
.error {
margin: 0;
color: #c0392b;
font-size: var(--responsive-caption, 13px);
line-height: 1.45;
}
.actions {
display: flex;
gap: var(--spacing-md, 12px);
width: 100%;
}
.button {
flex: 1 1 auto;
min-height: var(--pwa-button-height, 44px);
border: 0;
border-radius: var(--radius-bottom-sheet, 28px);
font-size: var(--responsive-body, 16px);
font-weight: 600;
cursor: pointer;
}
.button:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.secondary {
background: var(--color-text-secondary, #9e9e9e);
color: var(--color-page-background, #ffffff);
}
.primary {
background: linear-gradient(
var(--color-button-gradient-start, #ff67e0),
var(--color-button-gradient-end, #ff52a2)
);
color: var(--color-page-background, #ffffff);
}
@@ -0,0 +1,20 @@
export const stripePaymentDialogStyles = {
overlay:
"fixed inset-0 z-70 flex items-center justify-center bg-[rgba(0,0,0,0.5)] pb-[calc(var(--dialog-safe-margin,20px)+var(--app-safe-bottom,0px))] pl-[calc(var(--dialog-safe-margin,20px)+var(--app-safe-left,0px))] pr-[calc(var(--dialog-safe-margin,20px)+var(--app-safe-right,0px))] pt-[calc(var(--dialog-safe-margin,20px)+var(--app-safe-top,0px))]",
dialog:
"max-h-[calc(var(--app-visible-height,100dvh)-calc(var(--dialog-safe-margin,20px)*2))] w-full max-w-(--dialog-wide-max-width,420px) overflow-y-auto rounded-(--responsive-card-radius,32px) bg-(--color-page-background,#ffffff) px-(--responsive-card-padding,18px) pb-(--responsive-card-padding,18px) pt-(--responsive-card-padding-lg,24px) shadow-[0_18px_40px_rgba(0,0,0,0.16)]",
header: "mb-(--page-section-gap,18px) text-left",
title:
"m-0 mb-[clamp(7px,1.481vw,8px)] text-(length:--responsive-page-title,22px) font-bold leading-[1.2] text-(--color-text-foreground,#171717)",
content:
"m-0 text-(length:--responsive-body,14px) leading-[1.5] text-[#393939]",
form: "flex flex-col gap-(--page-section-gap,18px)",
error:
"m-0 text-(length:--responsive-caption,13px) leading-[1.45] text-[#c0392b]",
actions: "flex w-full gap-(--spacing-md,12px)",
button:
"min-h-(--pwa-button-height,44px) flex-auto cursor-pointer rounded-(--radius-bottom-sheet,28px) border-0 text-(length:--responsive-body,16px) font-semibold disabled:cursor-not-allowed disabled:opacity-55",
secondary: "bg-(--color-text-secondary,#9e9e9e) text-(--color-page-background,#ffffff)",
primary:
"bg-[linear-gradient(var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-(--color-page-background,#ffffff)",
} as const;
@@ -17,7 +17,7 @@ import { ExceptionHandler } from "@/core/errors";
import { ROUTES } from "@/router/routes";
import { Logger } from "@/utils";
import styles from "./stripe-payment-dialog.module.css";
import { stripePaymentDialogStyles as styles } from "./stripe-payment-dialog.styles";
const log = new Logger("SubscriptionStripePaymentDialog");
const stripePublishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
@@ -177,7 +177,10 @@ function StripePaymentForm({
<form className={styles.form} onSubmit={handleSubmit}>
<PaymentElement onLoadError={handleLoadError} />
{errorMessage ? (
<p role="alert" className={styles.error}>
<p
role="alert"
className={styles.error}
>
{errorMessage}
</p>
) : null}
@@ -12,7 +12,7 @@ import {
import { Logger } from "@/utils";
import { StripePaymentDialog } from "./stripe-payment-dialog";
import dialogStyles from "./stripe-payment-dialog.module.css";
import { stripePaymentDialogStyles as dialogStyles } from "./stripe-payment-dialog.styles";
import { SubscriptionCtaButton } from "./subscription-cta-button";
const log = new Logger("SubscriptionCheckoutButton");