import { Meta } from '@storybook/blocks';

<Meta title="Documentation/Less" />

# Less 规范

## Theme

### 自定义主题

`src/styles/theme.less` 定义了 UI 主题颜色变量、Less 函数、Less 混合。

在开发过程中使用这个文件的定义的变量、函数以及混合。通过 UmiJS 的配置，我们在 Less 文件不需要收到导入这个文件。

```css
// 颜色
@primary-color: #1664ff; // 主色调
@primary-color-secondary: #4e89ff;
@primary-color-hover: #69b1ff;
@sider-background-color: #f2f5f7; // 侧边栏背景颜色
@background-color: #f9fafb; // 页面背景颜色
@text-color: #1d1d20;
@text-color-secondary: #575757;
@text-color-tertiary: #8a8a8a;
@text-placeholder-color: rgba(0, 0, 0, 0.25);
@text-disabled-color: rgba(0, 0, 0, 0.25);
@success-color: #6ac21d;
@error-color: #c73131;
@warning-color: #f98e1b;
@abort-color: #8a8a8a;
@pending-color: #ecb934;
@underline-color: #5d93ff;
@border-color: #eaeaea;
@link-hover-color: #69b1ff;
@heading-color: rgba(0, 0, 0, 0.85);
@input-icon-hover-color: rgba(0, 0, 0, 0.85);

// 字体大小
@font-size-title: 18px;
@font-size-content: 16px;
@font-size: 15px;
@font-size-input: 14px;
@font-size-input-lg: @font-size-content;

// padding
@content-padding: 25px;
```



颜色变量还可以在 `js/ts/jsx/tsx` 里使用

```js
import themes from "@/styles/theme.less"

const primaryColor = themes['primaryColor']; // #1664ff
```

### Ant Design 主题覆盖

Ant Design 可以[定制主题](https://ant-design.antgroup.com/docs/react/customize-theme-cn)，Ant Design 是通过 [ConfigProvider](https://ant-design.antgroup.com/components/config-provider-cn) 组件进行主题定制，而 UmiJS 可以在[配置文件](https://umijs.org/docs/max/antd#%E6%9E%84%E5%BB%BA%E6%97%B6%E9%85%8D%E7%BD%AE)或者 [`app.ts`](https://umijs.org/docs/max/antd#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%85%8D%E7%BD%AE) 里进行配置。我选择在 [`app.ts`](https://umijs.org/docs/max/antd#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%85%8D%E7%BD%AE) 里进行配置，因为这里可以使用主题颜色变量。

```tsx
// 主题修改
export const antd: RuntimeAntdConfig = (memo) => {
  memo.theme ??= {};
  memo.theme.token = {
    colorPrimary: themes['primaryColor'],
    colorSuccess: themes['successColor'],
    colorError: themes['errorColor'],
    colorWarning: themes['warningColor'],
    colorLink: themes['primaryColor'],
    colorText: themes['textColor'],
    controlHeightLG: 46,
  };
  memo.theme.components ??= {};
  memo.theme.components.Tabs = {};
  memo.theme.components.Button = {
    defaultBg: 'rgba(22, 100, 255, 0.06)',
    defaultBorderColor: 'rgba(22, 100, 255, 0.11)',
    defaultColor: themes['textColor'],
    defaultHoverBg: 'rgba(22, 100, 255, 0.06)',
    defaultHoverBorderColor: 'rgba(22, 100, 255, 0.5)',
    defaultHoverColor: '#3F7FFF',
    defaultActiveBg: 'rgba(22, 100, 255, 0.12)',
    defaultActiveBorderColor: 'rgba(22, 100, 255, 0.75)',
    defaultActiveColor: themes['primaryColor'],
    contentFontSize: parseInt(themes['fontSize']),
  };
  memo.theme.components.Input = {
    inputFontSize: parseInt(themes['fontSizeInput']),
    inputFontSizeLG: parseInt(themes['fontSizeInputLg']),
    paddingBlockLG: 10,
  };
  memo.theme.components.Select = {
    singleItemHeightLG: 46,
    optionSelectedColor: themes['primaryColor'],
  };
  memo.theme.components.Table = {
    headerBg: 'rgba(242, 244, 247, 0.36)',
    headerBorderRadius: 4,
    rowSelectedBg: 'rgba(22, 100, 255, 0.05)',
  };
  memo.theme.components.Tabs = {
    titleFontSize: 16,
  };
  memo.theme.components.Form = {
    labelColor: 'rgba(29, 29, 32, 0.8);',
  };
  memo.theme.components.Breadcrumb = {
    iconFontSize: parseInt(themes['fontSize']),
    linkColor: 'rgba(29, 29, 32, 0.7)',
    separatorColor: 'rgba(29, 29, 32, 0.7)',
  };

  memo.theme.cssVar = true;

  memo.appConfig = {
    message: {
      // 配置 message 最大显示数，超过限制时，最早的消息会被自动关闭
      maxCount: 3,
    },
  };

  return memo;
};
```

覆盖 Ant Design 的默认样式，优先选择这种方式，如果没有相应的变量，才覆盖 Ant Design 的样式，在 `src/overrides.less` 文件里覆盖，请查看 UmiJS 关于[`global.less`](https://umijs.org/docs/guides/directory-structure#globalcsslesssassscss) 与 [`overrides.less`](https://umijs.org/docs/guides/directory-structure#overridescsslesssassscss) 的说明。

## BEM

类名遵循 [BEM - Block, Element, Modifier](https://getbem.com/) 规范

### Block

有意义的独立实体，Block 的类名由小写字母、数字和横线组成，比如 `model`、`form`、`paramneter-input`

### Element

块的一部分，Element 的类名由 `Block 的类名` + `双下划线（__）` + `Element 的名称` 组成，比如 `model__title`、`form__input`、`paramneter-input__content`

### Modifier

块或元素的变种，Modifier 的类名由 `Block 的类名` 或者 `Element 的类名` + `双横线（--）` + `Modifier 的名称` 组成，比如 `button--active`、`form--large`

举个 🌰

```tsx
// @/components/CodeConfigItem/index.tsx

import classNames from 'classnames';
import styles from './index.less';

function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
  return (
    <div className={styles['code-config-item']}>
      <Flex justify="space-between" align="center" style={{ marginBottom: '15px' }}>
        <Typography.Paragraph
          className={styles['code-config-item__name']}
          ellipsis={{ tooltip: item.code_repo_name }}
        >
          {item.code_repo_name}
        </Typography.Paragraph>
        <div
          className={classNames(
            styles['code-config-item__tag'],
            item.is_public
              ? styles['code-config-item__tag--public']
              : styles['code-config-item__tag--private'],
          )}
        >
          {item.is_public ? '公开' : '私有'}
        </div>
      </Flex>
      <Typography.Paragraph
        className={styles['code-config-item__url']}
        ellipsis={{ rows: 2, tooltip: item.git_url }}
      >
        {item.git_url}
      </Typography.Paragraph>
      <div className={styles['code-config-item__branch']}>{item.git_branch}</div>
    </div>
  );
}

```

### 一点建议

如果你陷入嵌套地狱，比如

```tsx
function Component() {
  return (
    <div className="component">
      <div className="component__element1">
        <div className="component__element1__element2">
          <div className="component__element1__element2__element3">
            <div className="component__element1__element2__element3__element4">
              // 等等
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
```

说明你需要拆分组件了

```tsx
function Component() {
  return (
    <div className="component">
      <div className="component__element1">
         <Component1></Component1>
      </div>
    </div>
  )
}

function SubComponent() {
  return (
    <div className="sub-component">
      <div className="sub-component__element1">
      </div>
    </div>
  )
}
```

既减少了类名的嵌套，又减少了 HTML 的嵌套，使代码逻辑更加清晰，易于理解与维护，同时实现模块化和组件化



