Fix global CSS bug silently disabling all Tailwind margin/padding utilities
index.css had a hand-written reset (`*, *::before, *::after { margin: 0;
padding: 0; box-sizing: border-box; }`) left over from before Tailwind was
added, sitting outside any `@layer` block. Per the CSS Cascade Layers spec,
unlayered rules always beat layered rules regardless of selector
specificity — since every Tailwind utility class lives inside `@layer
utilities`, this one `*`-selector rule was silently overriding every
single margin/padding utility (p-*, m-*, mx-auto, etc.) across the entire
app. Other properties (colors, borders, gap) weren't affected, which is
why this went unnoticed through the whole Tailwind migration — only
spacing/centering was silently broken, not colors or general layout.
Tailwind's own preflight (already loaded via `@import "tailwindcss"`,
correctly placed inside `@layer base`) provides the identical reset, so
the duplicate unlayered copy was simply deleted.
Also fixed two smaller, related issues found while investigating: an
inline `style={{ maxWidth: '100vw' }}` on Login/Register's auth-card
overrode its CSS class's `max-width: 400px`, stretching it to near full
viewport width on screens wider than ~400px (invisible on mobile, obvious
on laptop/desktop — probably what originally looked like "responsive
wasn't fixed"). And BacktestPage.tsx, unlike every other page, rendered
its `mx-auto`-centered container as a direct child of #root (which has
`display: flex; flex-direction: column`), so flexbox's default
align-items: stretch overrode the centering; wrapped it in the same
min-h-screen container pattern every other page already uses.
Verified via direct getComputedStyle()/getBoundingClientRect() measurement
(screenshots in this environment don't reliably reflect true layout) —
margin-inline resolved to 0px before the fix, 183px (correctly centered)
after. Frontend build clean, 170 backend tests unaffected (CSS-only change).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ export default function LoginPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<div className="auth-card" style={{ maxWidth: '100vw', boxSizing: 'border-box' }}>
|
<div className="auth-card">
|
||||||
<h1>{t('login.title')}</h1>
|
<h1>{t('login.title')}</h1>
|
||||||
<h2>{t('login.subtitle')}</h2>
|
<h2>{t('login.subtitle')}</h2>
|
||||||
{error && <div className="error-message">{error}</div>}
|
{error && <div className="error-message">{error}</div>}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function RegisterPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<div className="auth-card" style={{ maxWidth: '100vw', boxSizing: 'border-box' }}>
|
<div className="auth-card">
|
||||||
<h1>Trading Portal</h1>
|
<h1>Trading Portal</h1>
|
||||||
<h2>Create Account</h2>
|
<h2>Create Account</h2>
|
||||||
{error && <div className="error-message">{error}</div>}
|
{error && <div className="error-message">{error}</div>}
|
||||||
|
|||||||
@@ -161,7 +161,8 @@ export default function BacktestPage() {
|
|||||||
}, [exchange]);
|
}, [exchange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-[1000px] bg-bg-primary p-5 text-text-primary">
|
<div className="min-h-screen bg-bg-primary">
|
||||||
|
<div className="mx-auto max-w-[1000px] p-5 text-text-primary">
|
||||||
<h1 className="text-2xl font-bold text-text-heading">📊 {t('Backtest')}</h1>
|
<h1 className="text-2xl font-bold text-text-heading">📊 {t('Backtest')}</h1>
|
||||||
|
|
||||||
<div className="mb-5 mt-3 flex gap-1">
|
<div className="mb-5 mt-3 flex gap-1">
|
||||||
@@ -181,6 +182,7 @@ export default function BacktestPage() {
|
|||||||
symbols={symbols} tradeSize={tradeSize} setTradeSize={setTradeSize} />
|
symbols={symbols} tradeSize={tradeSize} setTradeSize={setTradeSize} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,14 +64,13 @@
|
|||||||
background-color: var(--bg-primary);
|
background-color: var(--bg-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Global Reset --- */
|
/* Global box-sizing/margin/padding reset is handled by Tailwind's own
|
||||||
*,
|
preflight (inside @layer base, from `@import "tailwindcss"` above).
|
||||||
*::before,
|
A hand-written unlayered copy of the same reset used to live here —
|
||||||
*::after {
|
CSS Cascade Layers give ANY unlayered rule priority over ALL layered
|
||||||
box-sizing: border-box;
|
rules regardless of selector specificity, so that unlayered `* { margin:
|
||||||
margin: 0;
|
0; padding: 0 }` was silently overriding every single Tailwind margin/
|
||||||
padding: 0;
|
padding utility class (p-*, m-*, mx-auto, etc.) app-wide. Removed. */
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Theo dõi đánh giá dự án Trading Portal — v9
|
||||||
|
|
||||||
|
> **Ngày đánh giá gốc:** 2026-07-03
|
||||||
|
> **Cập nhật v1-v8:** xem các file `theo_doi_trading-portal_v1.md`…`v8.md`
|
||||||
|
> **Cập nhật v9 (lần này):** 2026-07-04 — sửa 1 bug CSS toàn cục nghiêm trọng khiến **toàn bộ class margin/padding của Tailwind trên cả app bị vô hiệu hóa âm thầm**, cộng 2 bug nhỏ hơn liên quan (login/register card giãn to trên desktop, BacktestPage không canh giữa)
|
||||||
|
> **Người thực hiện:** Claude (Sonnet 5), theo yêu cầu của tien.a.le@accenture.com
|
||||||
|
> **Quy ước đặt tên:** Mỗi lần có thay đổi lớn → tạo bản mới `theo_doi_trading-portal_v10.md`, ... giữ nguyên các bản cũ làm lịch sử.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Tổng quan dự án
|
||||||
|
|
||||||
|
(Không đổi — xem [theo_doi_trading-portal_v2.md](theo_doi_trading-portal_v2.md) mục 1.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Bối cảnh
|
||||||
|
|
||||||
|
Người dùng phản ánh: "giao diện trên laptop to quá chừng" — nghi ngờ việc rà soát responsive ở v6 (mục 3, task #34) chưa thực sự đầy đủ. Kiểm tra trực tiếp bằng trình duyệt ở độ phân giải laptop thực tế (1366×768) thay vì chỉ dựa vào ảnh chụp màn hình, phát hiện **3 vấn đề riêng biệt**, xếp theo mức độ nghiêm trọng tăng dần khi đào sâu:
|
||||||
|
|
||||||
|
### 2.1 Bug nhỏ: `LoginPage`/`RegisterPage` có inline style thừa
|
||||||
|
|
||||||
|
`<div className="auth-card" style={{ maxWidth: '100vw', boxSizing: 'border-box' }}>` — inline style luôn thắng CSS class, nên `max-width: 100vw` đè lên `max-width: 400px` của class `.auth-card`, khiến khung đăng nhập giãn gần hết chiều rộng màn hình ở độ phân giải lớn (ẩn ở mobile vì 100vw ở đó vốn đã nhỏ, nên không ai để ý). `box-sizing: border-box` trong cùng inline style cũng thừa (đã có reset toàn cục). **Đã xóa cả 2 inline style** ở cả `LoginPage.tsx` và `RegisterPage.tsx`.
|
||||||
|
|
||||||
|
### 2.2 Bug nhỏ: `BacktestPage.tsx` không canh giữa
|
||||||
|
|
||||||
|
Trang này là trang **duy nhất** trong số các trang dùng pattern `mx-auto max-w-[Npx]` mà không có div bọc ngoài (không giống `AnalyticsPage`/`ProfilePage`/`AdminPage`/`AuditLogPage` đều có nav bar + wrapper `min-h-screen` bên ngoài). Vì `#root` có `display:flex; flex-direction:column`, div `mx-auto` trở thành flex item trực tiếp của `#root`, và `align-items` mặc định (`normal`→`stretch`) làm nó giãn full-width thay vì canh giữa. **Đã bọc thêm 1 div `min-h-screen bg-bg-primary` bên ngoài**, đúng theo pattern các trang khác.
|
||||||
|
|
||||||
|
### 2.3 Bug lớn — nghiêm trọng nhất: CSS reset viết tay đè lên toàn bộ Tailwind margin/padding utilities
|
||||||
|
|
||||||
|
Khi đào sâu tìm hiểu tại sao `mx-auto` "không hoạt động" ngay cả sau khi đã bọc div đúng cách ở mục 2.2, phát hiện: `index.css` có đoạn reset viết tay từ trước khi thêm Tailwind:
|
||||||
|
|
||||||
|
```css
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Đoạn này nằm **ngoài mọi `@layer`** của Tailwind (không nằm trong `@layer base/components/utilities`). Theo đặc tả CSS Cascade Layers: **style không thuộc layer nào luôn thắng mọi style thuộc layer nào đó, bất kể độ đặc hiệu (specificity) selector**. Vì TOÀN BỘ utility class của Tailwind (`.p-5`, `.mx-auto`, `.mb-5`, `.px-4`, v.v.) đều nằm trong `@layer utilities`, đoạn reset viết tay này (dù chỉ dùng selector `*` — độ đặc hiệu thấp nhất có thể) vẫn **đè lên và vô hiệu hóa TẤT CẢ margin/padding utility của Tailwind trên toàn bộ ứng dụng**.
|
||||||
|
|
||||||
|
**Vì sao không ai nhận ra ngay:** các thuộc tính khác (màu nền, viền, `gap` giữa các phần tử flex/grid, font, border-radius...) không bị ảnh hưởng (không phải margin/padding), nên giao diện vẫn "trông tạm ổn" ở cái nhìn thoáng qua — chỉ khi kiểm tra kỹ khoảng cách/canh giữa mới lộ ra. Đây chính xác là loại lỗi tạo cảm giác "giao diện to quá, không đúng chỗ" mà người dùng mô tả — hàng trăm chỗ dùng `p-*`/`m-*` xuyên suốt 14 file đã chuyển sang Tailwind đều bị ảnh hưởng.
|
||||||
|
|
||||||
|
**Cách sửa:** Tailwind v4's preflight (bên trong `@layer base`, tự động có sẵn từ `@import "tailwindcss"`) **đã cung cấp sẵn đúng loại reset y hệt** (`box-sizing:border-box; margin:0; padding:0` cho `*, ::before, ::after`), chỉ khác là nó nằm ĐÚNG trong layer, nên các utility trong `@layer utilities` (khai báo sau `base` trong thứ tự layer) mới override đúng theo thiết kế. → **Xóa hẳn đoạn reset viết tay trùng lặp** trong `index.css`, để Tailwind's preflight tự lo, không cần viết lại.
|
||||||
|
|
||||||
|
### 2.4 Kiểm chứng
|
||||||
|
|
||||||
|
Đo trực tiếp trong trình duyệt (không chỉ dựa vào ảnh chụp — ảnh chụp của công cụ preview trong phiên làm việc này không phản ánh chính xác layout thật, đã xác nhận qua đối chiếu với `getBoundingClientRect()`/`getComputedStyle()`):
|
||||||
|
|
||||||
|
| Trước khi sửa | Sau khi sửa |
|
||||||
|
|---|---|
|
||||||
|
| `.mx-auto` (BacktestPage): `margin-left/right: 0px`, giãn full 1366px | `margin-left/right: 183px`, đúng 1000px canh giữa |
|
||||||
|
| `.p-5`: `padding: 0px` | `padding: 20px` (đúng `5 × 4px` theo thang spacing Tailwind) |
|
||||||
|
| `.auth-card` (Login, sau fix 2.1): full width ~1268px | đúng 400px, canh giữa cả ngang lẫn dọc |
|
||||||
|
|
||||||
|
Build frontend sạch, 170 test backend vẫn pass (thay đổi chỉ ở CSS, không đụng logic).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Toàn bộ nhược điểm & rủi ro — bổ sung mới trong v9
|
||||||
|
|
||||||
|
| # | Vấn đề | Trạng thái |
|
||||||
|
|---|---|---|
|
||||||
|
| ee | (mới) Inline style `maxWidth:'100vw'` đè `.auth-card`'s `max-width:400px` trên Login/Register | ✅ Đã sửa (mục 2.1) |
|
||||||
|
| ff | (mới) `BacktestPage.tsx` thiếu wrapper, bị flex-stretch của `#root` làm mất canh giữa | ✅ Đã sửa (mục 2.2) |
|
||||||
|
| gg | (mới, **nghiêm trọng**) CSS reset viết tay không nằm trong `@layer`, vô hiệu hóa toàn bộ margin/padding utility của Tailwind app-wide | ✅ Đã sửa (mục 2.3) |
|
||||||
|
|
||||||
|
**Bài học quy trình:** việc rà soát responsive ở v6 (mục "Review UI end-to-end", task #34) dựa nhiều vào ảnh chụp màn hình ở kích thước mobile/tablet — bug này chỉ lộ rõ ở kích thước **laptop/desktop** (1280px+) và cần đo trực tiếp `getComputedStyle()`/`getBoundingClientRect()` mới phát hiện được, vì ảnh chụp không đáng tin cậy để kiểm tra chính xác khoảng cách/kích thước (đúng như mô tả của chính công cụ preview_screenshot). Từ nay nếu nghi ngờ vấn đề layout tinh vi, nên ưu tiên `preview_eval` + `getComputedStyle` thay vì chỉ nhìn ảnh chụp.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Lịch sử phiên bản
|
||||||
|
|
||||||
|
| Phiên bản | Ngày | Thay đổi |
|
||||||
|
|---|---|---|
|
||||||
|
| v0-v7 | 2026-07-03 — 2026-07-04 | Xem file tương ứng |
|
||||||
|
| v8 | 2026-07-04 | Wire ADX/regime vào bộ lọc tín hiệu dùng chung, hợp nhất 2 hệ thống regime. 170 test pass. |
|
||||||
|
| v9 | 2026-07-04 | Sửa bug CSS toàn cục: reset viết tay không nằm trong `@layer` vô hiệu hóa toàn bộ margin/padding utility Tailwind app-wide (gg) — nguyên nhân chính khiến giao diện "to quá" trên laptop. Kèm 2 bug nhỏ: inline style đè max-width trên Login/Register (ee), BacktestPage thiếu wrapper mất canh giữa (ff). Build sạch, 170 test pass (không đổi logic). |
|
||||||
Reference in New Issue
Block a user