SN.
همه نوشته ها
فرانت اند۱۶ مرداد ۱۴۰۵۸ دقیقه

ارتقای Tailwind CSS v4 بدون Regression؛ تغییر زیرساخت بدون خراب کردن UI

برای ارتقای Tailwind v4 مثل Redesign یا Search & Replace جلو نمی روم؛ اول Visual Baseline می سازم، بعد Tokenها، Build و رفتار Responsive/RTL را مرحله ای مهاجرت می دهم.

نمای نزدیک کد CSS روی مانیتور تیره

ارتقای یک Dependency زیرساختی برای من وقتی موفق است که کاربر در پایان تغییر ناخواسته ای نبیند.

ارتقای Tailwind CSS v4 مثال خوبی است. تغییر فقط چند Utility Class نیست؛ مدل Configuration، Theme Variableها و Build Integration نسبت به نسل قبلی متفاوت شده است. اگر هم زمان با این مهاجرت Typography، spacing یا Componentها را هم «بهتر» کنیم، بعد از اولین Regression دقیقاً نمی دانیم کدام تغییر مقصر بوده است.

برای همین Upgrade را از Redesign جدا می کنم و قبل از هر تغییر یک Visual Contract می سازم.

قبل از Dependency، محصول را Baseline می کنم

اول مشخص می کنم چه چیزهایی نباید تغییر کنند. برای یک Front-End Production معمولاً این فهرست را دارم:

  • رنگ ها و Design Tokenهای اصلی؛
  • Font Family، Weight، Line-height و نحوه Load شدن Font؛
  • عرض Containerها و Breakpointها؛
  • Grid و Stack شدن Componentها؛
  • Border، Radius و Shadowهای مهم؛
  • Hover، Focus-visible، Disabled و Active Stateها؛
  • رفتار RTL/LTR؛
  • صفحه های حساس مثل Header، Checkout، Dashboard یا Modal؛
  • Warningهای Build و خروجی Production موجود.

اگر ابزار Visual Regression خودکار وجود نداشته باشد، حداقل Screenshot مرجع برای Viewportهای اصلی و چند Flow کلیدی ثبت می کنم. هدف این نیست که Pixel Diff کامل بسازم؛ هدف این است که حافظه بصری جای Baseline واقعی را نگیرد.

در Tailwind v4 اول مدل Token را می فهمم

در Tailwind v4 مسیر CSS-first پررنگ تری وجود دارد و Theme Variableها می توانند با @theme تعریف شوند تا هم CSS Variable باشند و هم Utilityهای مرتبط را تولید کنند.

@import "tailwindcss";

@theme {
  --color-accent: #ff6a00;
  --breakpoint-3xl: 120rem;
}

در Migration، این Syntax برای من هدف نیست؛ یک سؤال مهم تر مطرح می کند: Tokenهای پروژه امروز واقعاً کجا زندگی می کنند؟

در پروژه قدیمی ممکن است رنگ Brand در Config، CSS Variable، Arbitrary Value و چند Component با چهار نام مختلف تکرار شده باشد. مهاجرت فرصت خوبی برای مشخص کردن Source of Truth است، اما نه برای عوض کردن خود Design.

ابتدا Mapping می سازم: Token قدیمی به Token جدید. بعد Consumerها را جابه جا می کنم. اگر هم زمان مقدار Token را هم تغییر بدهم، Upgrade و Redesign به هم گره می خورند.

اجرای Migration را در Sliceهای کوچک انجام می دهم

من معمولاً ترتیب کار را این طور نگه می دارم:

  1. Build Integration جدید را بالا می آورم.
  2. CSS پایه و Fontها را بدون تغییر ظاهری منتقل می کنم.
  3. Tokenها و Custom Utilityهای ضروری را Mapping می کنم.
  4. Componentهایی که Build یا Utility آن ها شکسته است اصلاح می کنم.
  5. Viewportها و Stateهای تعاملی را مقایسه می کنم.
  6. فقط بعد از رسیدن به Parity، Cleanupهای غیرضروری را بررسی می کنم.

این ترتیب عمداً کندتر از یک Replace بزرگ به نظر می رسد، اما Debug کردن Regression را بسیار سریع تر می کند.

بخش Typography یکی از اولین جاهایی است که Regression پنهان می شود

موضوع Font فقط font-family نیست. تغییر Build ممکن است Load Order، Variable Font، Fallback یا Preflight را طوری تغییر دهد که Heading چند Pixel متفاوت Wrap شود.

برای پروژه دو زبانه فارسی/انگلیسی این اختلاف مهم تر است. یک Scale که برای Fraunces خوب است الزاماً برای Yekan Bakh مناسب نیست. Line-height فشرده انگلیسی ممکن است در فارسی Clip یا فشردگی ایجاد کند.

در Upgrade زیرساختی این موارد را جداگانه چک می کنم:

  • Font واقعاً از همان Asset قبلی Load می شود؛
  • Weightهای مورد استفاده موجودند؛
  • line-height و Letter-spacing ناخواسته Reset نشده اند؛
  • Headingهای طولانی در عرض های مرزی Wrap قابل قبول دارند؛
  • Fallback قبل از Load Font باعث Jump قابل توجه نمی شود.

اگر Typography باید Redesign شود، آن را Task جدا ثبت می کنم؛ نه اینکه داخل Migration پنهانش کنم.

مفهوم Breakpoint فقط یک عدد در Config نیست

تغییر Breakpoint ممکن است صفحه را در 1024px یا 1280px کاملاً متفاوت کند، حتی اگر Desktop بزرگ سالم باشد.

من Viewportهای مرزی را عمداً تست می کنم: کمی قبل و بعد از هر Breakpoint. خیلی از Bugها دقیقاً جایی ظاهر می شوند که Grid هنوز Desktop است اما فضای واقعی برای Childها کافی نیست.

مواردی که دنبال می کنم:

  • min-widthهایی که باعث Horizontal Scroll می شوند؛
  • Grid Columnهایی که با متن بلند جمع نمی شوند؛
  • hidden/flexهایی که در Breakpoint اشتباه فعال می شوند؛
  • Imageهایی که Aspect Ratio یا object-fit متفاوت گرفته اند؛
  • Sticky Elementهایی که با Header هم پوشانی پیدا می کنند.

بررسی Responsive QA فقط چند Screenshot در 1440 و 390 نیست؛ رفتار بین آن ها هم مهم است.

وضعیت Hover و Focus را مثل Typography Baseline می کنم

مشکل Regression همیشه به Layout محدود نیست. Utility یا Variant تغییریافته می تواند Hover، Focus-visible، Disabled یا Group State را از بین ببرد و صفحه در Screenshot عادی کاملاً سالم به نظر برسد.

برای Componentهای تعاملی این Stateها را مشخص تست می کنم:

  • Link و Button با Keyboard؛
  • Focus Ring و Contrast؛
  • Hover فقط روی Deviceهایی که آن را دارند؛
  • Disabled State بدون کاهش بیش از حد خوانایی؛
  • Group/Peer interaction؛
  • Transitionهایی که Property درست را Animate می کنند.

اگر پروژه Accessibility دارد، Upgrade نباید آن را به Feature اختیاری تبدیل کند.

در RTL محدودیت Physical Utilityها خودشان را نشان می دهند

در پروژه فارسی صرف dir="rtl" کافی نیست. Classهایی که بر اساس left/right نوشته شده اند ممکن است بعد از Refactor دقیقاً جهت اشتباه را بگیرند.

من هنگام Migration دنبال Physical Assumptionها می گردم:

  • ml/mr در جایی که ms/me منطقی تر است؛
  • left/right برای Badge یا Decoration؛
  • Arrowهایی که باید بر اساس Locale عوض شوند؛
  • Gradientی که جهت Composition آن به محل تصویر وابسته است؛
  • متن فنی LTR داخل Paragraph فارسی.

اما هدف Migration «RTL کردن دوباره سایت» نیست. فقط مطمئن می شوم مدل جدید همان رفتار تأییدشده را حفظ می کند.

تنظیم Build Configuration را جدا از UI Audit می کنم

در Tailwind v4 یکپارچگی با CSS و Build Tool مدرن تر شده است، اما هر پروژه Legacy فرض های خودش را دارد. قبل از حذف Config یا Plugin قدیمی بررسی می کنم آیا واقعاً مصرف می شود.

موارد مهم:

  • PostCSS/Vite integration فعلی؛
  • Pluginهای Tailwind یا Custom Utilityها؛
  • Source Detection و Classهای داینامیک؛
  • CSS Preprocessorهای قدیمی؛
  • Import Order؛
  • Minification و Production Output؛
  • Browser Support پروژه.

در Tailwind v4 تکیه بیشتری بر قابلیت های CSS مدرن وجود دارد و برای Browserهای قدیمی محدودیت بیشتری دیده می شود. اگر محصول الزام Browser قدیمی دارد، «آخرین نسخه بودن» دلیل کافی برای Upgrade نیست. Compatibility Requirement بخشی از تصمیم فنی است.

کلاس های داینامیک را قبل از Build Production پیدا می کنم

یکی از Bugهای خطرناک Utility-first این است که Class در Development به نظر درست بیاید اما Source Detection نتواند رشته ساخته شده در Runtime را تشخیص دهد.

به جای ساختن نام Class با Template Stringهای باز، Variantهای محدود را Map می کنم:

const toneClass = {
  danger: "text-red-500 border-red-500/30",
  success: "text-emerald-500 border-emerald-500/30",
};

این الگو فقط برای Tailwind v4 نیست؛ Dependency Upgrade زمان خوبی برای پیدا کردن فرض های Build است که قبلاً تصادفی کار می کردند.

فهرست Checklist مربوط به Regression بعد از Migration

بررسی Visual

  • Color و Tokenها با Baseline یکی هستند.
  • Typography و Wrapping در زبان های پروژه تغییر ناخواسته ندارد.
  • Border، Radius، Shadow و Opacity ثابت مانده اند.
  • Image Crop و Aspect Ratio درست اند.

بررسی Responsive

  • Mobile، Tablet و Desktop سالم اند.
  • عرض های نزدیک Breakpointها Horizontal Scroll ندارند.
  • Grid و Navigation در نقاط مرزی تست شده اند.
  • RTL/LTR در Layoutهای حساس درست است.

بررسی Interaction

  • Hover، Focus-visible، Active و Disabled کار می کنند.
  • Animation و Transition از بین نرفته اند.
  • Keyboard Navigation حفظ شده است.

بررسی Build

  • Production Build موفق است.
  • Warning جدید بدون توضیح وجود ندارد.
  • Asset Path و Font Path درست اند.
  • Dependency قدیمی ای که هنوز لازم است تصادفی حذف نشده است.
  • Browser Target با Requirement محصول سازگار است.

چه زمانی Upgrade را عقب می اندازم؟

اگر پروژه Browser قدیمی اجباری دارد، Release حساس نزدیک است، Test Coverage کافی نیست یا تیم هنوز Baseline ظاهری ندارد، تأخیر Upgrade می تواند تصمیم حرفه ای تری باشد.

هدف مهندسی استفاده از جدیدترین نسخه در کوتاه ترین زمان نیست. هدف کم کردن ریسک و نگه داشتن محصول در وضعیتی است که توسعه بعدی قابل پیش بینی باشد.

جمع بندی

برای من Tailwind CSS v4 بیشتر از یک موضوع Syntax است؛ یک Case Study خوب برای تغییر زیرساخت بدون تغییر محصول است.

اول Baseline، بعد Mapping، سپس Migration مرحله ای و در پایان Cleanup. وقتی این ترتیب حفظ شود، می توان Build و Design Tokenها را مدرن کرد بدون اینکه UI فعلی هزینه آزمایش ابزار جدید را پرداخت کند.

منابع رسمی برای بررسی بیشتر