Frontend Styling

XinAdmin uses Tailwind CSS v4 + Ant Design to build its styling system, controlling style priority through CSS Layers and enabling component-level style customization via Ant Design's Semantic DOM.

For more details on Ant Design styling, refer to Ant Design Compatible Style Documentation.

Style Architecture

┌──────────────────────────────────────────┐
│           Application Style Layer         │
│  Tailwind utilities + custom styles prop  │
│  Highest priority, overrides antd defaults│
├──────────────────────────────────────────┤
│           Ant Design Style Layer          │
│  CSS-in-JS generated component styles     │
│  ConfigProvider theme token global control│
├──────────────────────────────────────────┤
│              Base Style Layer             │
│  Tailwind base (reset + global styles)    │
└──────────────────────────────────────────┘

1. Tailwind CSS v4

1.1 Integration

XinAdmin integrates Tailwind CSS v4 via the Vite plugin, requiring no tailwind.config file:

// vite.config.ts
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),  // Tailwind v4 Vite plugin
  ],
});

1.2 CSS Layer Configuration

/* web/index.css */
@layer theme, base, antd, components, utilities;
@import "tailwindcss";
LayerPurposePriority
themeCSS variables and theme definitionsLowest
baseTailwind base reset stylesLow
antdAnt Design CSS-in-JS stylesMedium
componentsCustom component stylesHigh
utilitiesTailwind utility classesHighest

Key design: Placing antd layer before components and utilities ensures Tailwind utilities and custom component styles can override Ant Design defaults without needing !important.

Note: If Ant Design's CSS-in-JS output is not placed in the antd layer, wrap your app with <StyleProvider layer> from @ant-design/cssinjs. See Ant Design Compatible Style Documentation.

1.3 Tailwind v4 Features

The project uses these Tailwind v4 features:

FeatureExampleDescription
Arbitrary valuestext-[20px], bg-[#1677ff]Direct arbitrary values, no extend needed
New utilitiesbackdrop-blur-xs, size-14New v4 utility classes
@layer declaration@layer antd { ... }Custom layer priority control

1.4 Common Utility Classes

Widely used Tailwind classes across the project:

CategoryCommon Classes
Layoutflex, grid, items-center, justify-between, shrink-0, box-border
Positionrelative, sticky, z-1, z-10, z-999
Sizew-full, h-full, size-14, h-8, h-16
Spacingp-2, m-2.5, mt-5, mb-1.5, gap-6, pl-5
Texttruncate, text-center, font-semibold, text-[20px]
Borderrounded-sm, rounded-lg, rounded-xl, rounded-full
Overflowoverflow-hidden, overflow-y-auto, overflow-x-hidden
Responsivelg:grid-cols-4, md:grid-cols-2

1.5 Using Tailwind with Ant Design

// Quick layout and spacing adjustments with Tailwind classes
<Card className="mb-5 p-2.5">
  <Space className="flex justify-between items-center">
    <Button type="primary" className="rounded-lg font-semibold">
      Submit
    </Button>
    <Input className="w-60" placeholder="Search" />
  </Space>
</Card>

2. Ant Design Theme System

2.1 ConfigProvider Global Theme

The AntdProvider component manages global theme tokens:

// components/AntdProvider/index.tsx
const theme: ThemeConfig = useMemo(() => ({
  components: {
    Layout: {
      headerHeight: themeConfig.headerHeight,
      bodyBg: themeConfig.bodyBg,
      headerBg: themeConfig.headerBg,
      siderBg: themeConfig.siderBg,
      // ...
    },
    Menu: {
      activeBarBorderWidth: 0,
      itemBg: 'transparent',
      subMenuItemBg: 'transparent',
    },
  },
  token: {
    colorPrimary: themeConfig.colorPrimary,
    colorBgBase: themeConfig.colorBg,
    colorTextBase: themeConfig.colorText,
    borderRadius: themeConfig.borderRadius,
    controlHeight: themeConfig.controlHeight,
    // ... error / success / warning / info / link
  },
  algorithm: themeConfig.algorithm ? algorithm[themeConfig.algorithm] : undefined,
}), [themeConfig]);

2.2 Tokens and Algorithms

TokenDescriptionDefault
colorPrimaryBrand color#1677ff
colorBgBaseBase background#fff
colorTextBaseBase text color#000
borderRadiusGlobal border radius10
controlHeightControl height32
algorithmTheme algorithmdefaultAlgorithm

Four theme algorithms:

AlgorithmDescription
defaultAlgorithmDefault light
darkAlgorithmDark mode
defaultCompactAlgorithmLight + compact
darkCompactAlgorithmDark + compact

2.3 useToken Runtime Theme Values

Get current theme token values for dynamic inline styles:

import { theme } from 'antd';

const { useToken } = theme;

function MyComponent() {
  const { token } = useToken();

  return (
    <div
      style={{
        background: token.colorPrimaryBg,
        color: token.colorText,
        borderColor: token.colorBorder,
        borderRadius: token.borderRadius,
        boxShadow: token.boxShadow,
      }}
    >
      Content
    </div>
  );
}

Places where useToken is used in the project:

  • layout/SettingDrawer.tsx — Layout thumbnails in theme settings
  • layout/ColumnsMenu.tsx — Selected highlight color in columns menu
  • layout/HeaderRightRender.tsx — Divider color in search modal
  • pages/dashboard/analysis.tsx — Dashboard cards and chart colors

2.4 Theme Persistence

Theme configuration is persisted to localStorage via Zustand store:

// stores/global/index.ts
const useGlobalStore = create<GlobalStore>()(
  persist(
    (...args) => ({ ...globalState, ...globalAction(...args) }),
    {
      name: 'global-storage',
      storage: createJSONStorage(() => localStorage),
    }
  )
);

Users can visually modify theme settings via SettingDrawer. All changes take effect in real-time with 400ms debounce and auto-save.


3. Ant Design Semantic DOM

3.1 What is Semantic DOM

Ant Design v5+ defines stable semantic names for each component's internal elements. The classNames and styles props allow precise style overrides targeting specific child elements without depending on internal DOM class names.

// Precise style overrides using Semantic DOM
<Input
  classNames={{
    root: 'my-custom-input-root',        // Component root element
    input: 'my-custom-input-inner',       // Input element itself
    prefix: 'my-custom-input-prefix',     // Prefix area
    suffix: 'my-custom-input-suffix',     // Suffix area
  }}
  styles={{
    root: { marginBottom: 16 },
    input: { fontSize: 14, color: '#333' },
    prefix: { color: '#999' },
  }}
/>

3.2 Common Component Semantic Names

ComponentSemantic NamesNotes
Buttonroot, icon, contenticon includes loading icon
Inputroot, prefix, suffix, input, count
Input.SearchAbove + buttonbutton supports further nesting
Input.PasswordAbove + suffixsuffix includes visibility toggle
InputNumberroot, prefix, suffix, input, actionsactions includes up/down buttons
Input.TextArearoot, textarea, count
Selectroot, selector, selectionItem, arrow, clear
Modalroot, header, body, footer, close
Drawerroot, header, body, footer
Tableroot, header, body, summary, row, cell

3.3 Usage of styles in the Project

The project uses Semantic DOM's styles prop in several places:

// MobileDrawerMenu.tsx - Custom Drawer sub-area styles
<Drawer
  styles={{
    section: { width: 280 },
    header: {
      borderBottom: '1px solid ' + themeConfig.colorBorder,
      background: themeConfig.siderBg,
      color: themeConfig.siderColor,
    },
    body: { padding: 0, background: themeConfig.siderBg },
  }}
/>

// XinTable/index.tsx - Custom Modal header spacing
<XinForm
  modalProps={{
    styles: { header: { marginBottom: 16 } },
  }}
/>

// SettingDrawer.tsx - Adjust Drawer body top padding
<Drawer styles={{ body: { paddingTop: 10 } }} />

3.4 Combining classNames with Tailwind

classNames integrates seamlessly with Tailwind CSS for quick component style overrides:

<Button
  classNames={{
    root: 'bg-primary rounded-lg hover:opacity-90',
    icon: 'text-white/90',
    content: 'font-semibold',
  }}
  icon={<PlusOutlined />}
>
  Create
</Button>

<Input
  classNames={{
    root: 'shadow-sm hover:shadow-md transition-shadow',
    input: 'font-mono text-sm',
  }}
/>

<Modal
  classNames={{
    body: 'px-8 py-6',
    footer: 'border-t border-gray-100',
  }}
/>

4. Style Override Strategies

XinAdmin uses three style override approaches for different scenarios:

ApproachUse CasePriority
ConfigProvider themeGlobal brand colors, border radius, control heightLowest (default changes)
Semantic DOM stylesSpecific component child styles (e.g., Drawer header background)Medium (dynamic theme values)
Semantic DOM classNames + TailwindQuick layout/spacing/color overridesHigh (utilities layer priority)
Inline style propDynamically computed theme color valuesHighest (inline style priority)
  1. Global theme → Use ConfigProvider theme tokens
  2. Component defaults → Use ConfigProvider components component-level tokens
  3. Single component customization → Use classNames + Tailwind utilities
  4. Dynamic theme colors → Use styles or useToken + inline style

Global Style Override Example

/* Override antd component library global defaults if needed */
@layer components {
  .ant-table-thead > tr > th {
    font-weight: 600;
    text-transform: uppercase;
    font-size: 12px;
    letter-spacing: 0.05em;
  }
}

5. Custom Loading Styles

The project overrides Ant Design Spin component animation styles via a classic CSS file:

/* components/Loading/index.css */
.ant-spin-dot-item {
  background: var(--ant-color-primary);  /* Follows theme color */
}

.ant-spin-spinning {
  /* Custom animation */
}

Uses Ant Design's CSS variable system --ant-color-primary to ensure the loading indicator follows theme color changes.


6. References