refactor(frontend): consolidate shared page-shell rules into one stylesheet

Every panel page CSS file repeated the same wrapper boilerplate — the
--bg-page/--bg-card token triples for light/dark/ultra-dark, the
min-height + background root rule, the .ant-layout transparent reset,
the .content-shell transparent reset, and the .loading-spacer min-height.
That's ~30 identical lines duplicated across IndexPage, ClientsPage,
InboundsPage, XrayPage, SettingsPage, NodesPage, and ApiDocsPage.

Move all of it into styles/page-shell.css and import it once from
main.tsx alongside utils.css and page-cards.css. Each page CSS file
now only contains genuinely page-specific rules (content-area padding
overrides, page-specific tokens like ApiDocs's Swagger --sw-* set).

Also drop the per-page `import '@/styles/page-cards.css'` statements
from the 7 page tsx files now that main.tsx loads it globally.

Net: -211 deleted, +6 inserted in the touched files, plus the new
page-shell.css. .zero-margin (Divider override used by Nord/Warp
modals) folded into utils.css alongside the margin classes.
This commit is contained in:
MHSanaei 2026-05-25 14:04:29 +02:00
parent 21e1be43bd
commit 4e7cecb71b
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
19 changed files with 71 additions and 211 deletions

View file

@ -3,6 +3,8 @@ import { RouterProvider } from 'react-router-dom';
import { message } from 'antd';
import 'antd/dist/reset.css';
import '@/styles/utils.css';
import '@/styles/page-shell.css';
import '@/styles/page-cards.css';
import { setupAxios } from '@/api/axios-init';
import { readyI18n } from '@/i18n/react';

View file

@ -1,13 +1,4 @@
.api-docs-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.api-docs-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
--sw-bg: #1f2026;
--sw-bg-soft: #25272e;
--sw-bg-input: #15161a;
@ -22,8 +13,6 @@
}
.api-docs-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
--sw-bg: #0a0a0d;
--sw-bg-soft: #131316;
--sw-bg-input: #050507;

View file

@ -5,7 +5,6 @@ import 'swagger-ui-react/swagger-ui.css';
import { useTheme } from '@/hooks/useTheme';
import AppSidebar from '@/components/AppSidebar';
import '@/styles/page-cards.css';
import './ApiDocsPage.css';
const basePath = window.X_UI_BASE_PATH || '';

View file

@ -1,29 +1,3 @@
.clients-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.clients-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.clients-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.clients-page .ant-layout,
.clients-page .ant-layout-content {
background: transparent;
}
.clients-page .content-shell {
background: transparent;
}
.clients-page .content-area {
padding: 24px;
}
@ -39,10 +13,6 @@
min-width: 100px;
}
.clients-page .loading-spacer {
min-height: calc(100vh - 120px);
}
.clients-page .summary-card {
padding: 16px;
}

View file

@ -58,7 +58,6 @@ const ClientInfoModal = lazy(() => import('./ClientInfoModal'));
const ClientQrModal = lazy(() => import('./ClientQrModal'));
const ClientBulkAddModal = lazy(() => import('./ClientBulkAddModal'));
const ClientBulkAdjustModal = lazy(() => import('./ClientBulkAdjustModal'));
import '@/styles/page-cards.css';
import './ClientsPage.css';
const FILTER_STATE_KEY = 'clientsFilterState';

View file

@ -1,44 +1,13 @@
.inbounds-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.inbounds-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.inbounds-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.inbounds-page .ant-layout,
.inbounds-page .ant-layout-content {
background: transparent;
}
.content-shell {
background: transparent;
}
.content-area {
.inbounds-page .content-area {
padding: 24px;
}
@media (max-width: 768px) {
.content-area {
.inbounds-page .content-area {
padding: 8px;
}
}
.loading-spacer {
min-height: calc(100vh - 120px);
}
.summary-card {
padding: 16px;
}

View file

@ -36,7 +36,6 @@ import LazyMount from '@/components/LazyMount';
const InboundFormModal = lazy(() => import('./InboundFormModal'));
const InboundInfoModal = lazy(() => import('./InboundInfoModal'));
const QrCodeModal = lazy(() => import('./QrCodeModal'));
import '@/styles/page-cards.css';
import './InboundsPage.css';
type RowAction =

View file

@ -1,30 +1,3 @@
.index-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.index-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.index-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.index-page .ant-layout,
.index-page .ant-layout-content {
background: transparent;
}
.index-page .content-shell {
background: transparent;
}
.index-page .content-area {
padding: 24px;
}
@ -36,10 +9,6 @@
}
}
.index-page .loading-spacer {
min-height: calc(100vh - 120px);
}
.index-page .action {
cursor: pointer;
justify-content: center;

View file

@ -53,7 +53,6 @@ const SystemHistoryModal = lazy(() => import('./SystemHistoryModal'));
const XrayMetricsModal = lazy(() => import('./XrayMetricsModal'));
const XrayLogModal = lazy(() => import('./XrayLogModal'));
const VersionModal = lazy(() => import('./VersionModal'));
import '@/styles/page-cards.css';
import './IndexPage.css';
export default function IndexPage() {

View file

@ -1,29 +1,3 @@
.nodes-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.nodes-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.nodes-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.nodes-page .ant-layout,
.nodes-page .ant-layout-content {
background: transparent;
}
.nodes-page .content-shell {
background: transparent;
}
.nodes-page .content-area {
padding: 24px;
}
@ -34,10 +8,6 @@
}
}
.nodes-page .loading-spacer {
min-height: calc(100vh - 120px);
}
.nodes-page .summary-card {
padding: 16px;
}

View file

@ -17,7 +17,6 @@ import AppSidebar from '@/components/AppSidebar';
import NodeList from './NodeList';
import NodeFormModal from './NodeFormModal';
import { setMessageInstance } from '@/utils/messageBus';
import '@/styles/page-cards.css';
import './NodesPage.css';
export default function NodesPage() {

View file

@ -1,37 +1,7 @@
.settings-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.settings-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.settings-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.settings-page .ant-layout,
.settings-page .ant-layout-content {
background: transparent;
}
.settings-page .content-shell {
background: transparent;
}
.settings-page .content-area {
padding: 24px;
}
.settings-page .loading-spacer {
min-height: calc(100vh - 120px);
}
.settings-page .conf-alert {
margin-bottom: 10px;
}

View file

@ -35,7 +35,6 @@ import SecurityTab from './SecurityTab';
import TelegramTab from './TelegramTab';
import SubscriptionGeneralTab from './SubscriptionGeneralTab';
import SubscriptionFormatsTab from './SubscriptionFormatsTab';
import '@/styles/page-cards.css';
import './SettingsPage.css';
interface ApiMsg {

View file

@ -22,10 +22,6 @@
background: var(--ant-color-fill-tertiary);
}
.zero-margin {
margin: 0;
}
.server-row {
display: inline-flex;
align-items: center;

View file

@ -22,10 +22,6 @@
background: var(--ant-color-fill-tertiary);
}
.zero-margin {
margin: 0;
}
.license-actions {
display: flex;
align-items: center;

View file

@ -1,38 +1,7 @@
.xray-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.xray-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.xray-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.xray-page .ant-layout,
.xray-page .ant-layout-content {
background: transparent;
}
.xray-page .content-shell {
background: transparent;
}
.xray-page .content-area {
padding: 24px;
}
.xray-page .loading-spacer {
min-height: calc(100vh - 120px);
}
.xray-page .header-row {
display: flex;
flex-wrap: wrap;

View file

@ -44,7 +44,6 @@ import BalancersTab from './BalancersTab';
import DnsTab from './DnsTab';
import WarpModal from './WarpModal';
import NordModal from './NordModal';
import '@/styles/page-cards.css';
import './XrayPage.css';
const TAB_KEYS = ['tpl-basic', 'tpl-routing', 'tpl-outbound', 'tpl-balancer', 'tpl-dns', 'tpl-advanced'];

View file

@ -0,0 +1,65 @@
.index-page,
.clients-page,
.inbounds-page,
.xray-page,
.settings-page,
.nodes-page,
.api-docs-page {
--bg-page: #e6e8ec;
--bg-card: #ffffff;
min-height: 100vh;
background: var(--bg-page);
}
.index-page.is-dark,
.clients-page.is-dark,
.inbounds-page.is-dark,
.xray-page.is-dark,
.settings-page.is-dark,
.nodes-page.is-dark,
.api-docs-page.is-dark {
--bg-page: #1a1b1f;
--bg-card: #23252b;
}
.index-page.is-dark.is-ultra,
.clients-page.is-dark.is-ultra,
.inbounds-page.is-dark.is-ultra,
.xray-page.is-dark.is-ultra,
.settings-page.is-dark.is-ultra,
.nodes-page.is-dark.is-ultra,
.api-docs-page.is-dark.is-ultra {
--bg-page: #000;
--bg-card: #101013;
}
.index-page .ant-layout,
.index-page .ant-layout-content,
.clients-page .ant-layout,
.clients-page .ant-layout-content,
.inbounds-page .ant-layout,
.inbounds-page .ant-layout-content,
.xray-page .ant-layout,
.xray-page .ant-layout-content,
.settings-page .ant-layout,
.settings-page .ant-layout-content,
.nodes-page .ant-layout,
.nodes-page .ant-layout-content,
.api-docs-page .ant-layout,
.api-docs-page .ant-layout-content {
background: transparent;
}
.index-page .content-shell,
.clients-page .content-shell,
.inbounds-page .content-shell,
.xray-page .content-shell,
.settings-page .content-shell,
.nodes-page .content-shell,
.api-docs-page .content-shell {
background: transparent;
}
.loading-spacer {
min-height: calc(100vh - 120px);
}

View file

@ -13,3 +13,5 @@
.my-8 { margin: 8px 0; }
.my-10 { margin: 10px 0; }
.zero-margin { margin: 0; }