@qlover/create-app 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/configs/_common/.github/workflows/general-check.yml +17 -29
  3. package/configs/_common/.github/workflows/{release.yml.template → release.yml} +16 -51
  4. package/package.json +3 -3
  5. package/templates/pack-app/fe-config.json +30 -1
  6. package/templates/pack-app/package.json +45 -32
  7. package/templates/pack-app/tsconfig.test.json +1 -1
  8. package/templates/react-app/config/Identifier.I18n.ts +878 -0
  9. package/templates/react-app/config/app.router.json +7 -7
  10. package/templates/react-app/config/common.ts +7 -4
  11. package/templates/react-app/config/theme.json +7 -88
  12. package/templates/react-app/package.json +9 -4
  13. package/templates/react-app/postcss.config.js +1 -2
  14. package/templates/react-app/public/locales/en/common.json +118 -1
  15. package/templates/react-app/public/locales/zh/common.json +118 -1
  16. package/templates/react-app/src/App.tsx +14 -2
  17. package/templates/react-app/src/base/cases/RequestLogger.ts +3 -4
  18. package/templates/react-app/src/base/cases/RequestStatusCatcher.ts +3 -2
  19. package/templates/react-app/src/base/services/I18nService.ts +31 -3
  20. package/templates/react-app/src/base/services/ProcesserService.ts +3 -2
  21. package/templates/react-app/src/core/IOC.ts +15 -9
  22. package/templates/react-app/src/core/bootstrap.ts +42 -53
  23. package/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +14 -0
  24. package/templates/react-app/src/core/bootstraps/index.ts +36 -7
  25. package/templates/react-app/src/core/globals.ts +4 -6
  26. package/templates/react-app/src/core/registers/RegisterApi.ts +2 -5
  27. package/templates/react-app/src/core/registers/RegisterCommon.ts +38 -28
  28. package/templates/react-app/src/core/registers/RegisterControllers.ts +5 -10
  29. package/templates/react-app/src/core/registers/RegisterGlobals.ts +15 -14
  30. package/templates/react-app/src/core/registers/index.ts +27 -12
  31. package/templates/react-app/src/main.tsx +1 -1
  32. package/templates/react-app/src/pages/404.tsx +1 -1
  33. package/templates/react-app/src/pages/500.tsx +1 -1
  34. package/templates/react-app/src/pages/auth/Login.tsx +128 -36
  35. package/templates/react-app/src/pages/base/About.tsx +5 -2
  36. package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +38 -19
  37. package/templates/react-app/src/pages/base/Executor.tsx +447 -29
  38. package/templates/react-app/src/pages/base/Home.tsx +99 -93
  39. package/templates/react-app/src/pages/base/JSONStorage.tsx +47 -38
  40. package/templates/react-app/src/pages/base/Layout.tsx +5 -2
  41. package/templates/react-app/src/pages/base/Request.tsx +90 -208
  42. package/templates/react-app/src/pages/base/components/BaseHeader.tsx +13 -5
  43. package/templates/react-app/src/styles/css/page.css +11 -0
  44. package/templates/react-app/src/styles/css/tailwind.css +5 -0
  45. package/templates/react-app/src/styles/css/themes/_default.css +200 -0
  46. package/templates/react-app/src/styles/css/themes/dark.css +154 -0
  47. package/templates/react-app/src/styles/css/themes/index.css +3 -0
  48. package/templates/react-app/src/styles/css/themes/pink.css +160 -0
  49. package/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +56 -0
  50. package/templates/react-app/src/uikit/components/Loading.tsx +27 -21
  51. package/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +63 -13
  52. package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
  53. package/templates/react-app/src/uikit/controllers/RouterController.ts +3 -3
  54. package/templates/react-app/src/uikit/controllers/UserController.ts +1 -1
  55. package/templates/react-app/tailwind.config.js +1 -15
  56. package/templates/react-app/vite.config.ts +9 -3
  57. package/templates/react-app/lib/tailwind/root10px.js +0 -178
  58. package/templates/react-app/lib/tailwind/theme-generator.js +0 -238
  59. package/templates/react-app/public/locales/en/about.json +0 -3
  60. package/templates/react-app/public/locales/en/executor.json +0 -6
  61. package/templates/react-app/public/locales/en/home.json +0 -10
  62. package/templates/react-app/public/locales/en/jsonStorage.json +0 -11
  63. package/templates/react-app/public/locales/en/login.json +0 -7
  64. package/templates/react-app/public/locales/en/request.json +0 -15
  65. package/templates/react-app/public/locales/zh/about.json +0 -3
  66. package/templates/react-app/public/locales/zh/executor.json +0 -7
  67. package/templates/react-app/public/locales/zh/home.json +0 -10
  68. package/templates/react-app/public/locales/zh/jsonStorage.json +0 -11
  69. package/templates/react-app/public/locales/zh/login.json +0 -8
  70. package/templates/react-app/public/locales/zh/request.json +0 -15
  71. package/templates/react-app/src/base/port/InversifyIocInterface.ts +0 -9
  72. package/templates/react-app/src/uikit/styles/css/page.css +0 -3
  73. package/templates/react-app/src/uikit/styles/css/tailwind.css +0 -3
  74. /package/templates/react-app/config/{ErrorIdentifier.ts → Identifier.Error.ts} +0 -0
  75. /package/templates/react-app/src/{uikit/styles → styles}/css/index.css +0 -0
@@ -1,178 +0,0 @@
1
- import plugin from 'tailwindcss/plugin';
2
-
3
- /**
4
- * Tailwind CSS 配置 - 10px 根字体大小
5
- *
6
- * 注意事项:
7
- * 1. 所有的 rem 值都基于 10px 计算
8
- * 2. 16px = 1.6rem
9
- * 3. spacing 中的值需要乘以 1.6 来保持与原来的单位一致
10
- *
11
- * 由于移动端开发需求,方便计算,重置像素 10px, 将默认 16px 变成 10px
12
- *
13
- * 但是 tailwind 中的单位间距都是有个 rem 做单位, 16px 变成 10px 基数改变, 需要在原来不变像素上用 10px 基数重写一遍
14
- *
15
- * @see https://redpixelthemes.com/blog/change-tailwindcss-base-font-size/
16
- * @type {import('tailwindcss').Config['theme']}
17
- *
18
- * @example
19
- * // 使用示例
20
- * text-base -> 1.6rem -> 16px
21
- * p-4 -> 1.6rem -> 16px
22
- * w-40 -> 16rem -> 160px
23
- */
24
- const root10pxThemes = {
25
- lineHeight: {
26
- 3: '1.2rem',
27
- 4: '1.6rem',
28
- 5: '2.0rem',
29
- 6: '2.4rem',
30
- 7: '2.8rem',
31
- 8: '3.2rem',
32
- 9: '3.6rem',
33
- 10: '4.0rem',
34
- none: 1,
35
- tight: 1.25,
36
- snug: 1.375,
37
- normal: 1.5,
38
- relaxed: 1.625,
39
- loose: 2
40
- },
41
- maxWidth: {
42
- xs: '32.0rem',
43
- sm: '38.4rem',
44
- md: '44.8rem',
45
- lg: '51.2rem',
46
- xl: '57.6rem',
47
- '2xl': '67.2rem',
48
- '3xl': '76.8rem',
49
- '4xl': '89.6rem',
50
- '5xl': '102.4rem',
51
- '6xl': '115.2rem',
52
- '7xl': '128.0rem'
53
- },
54
-
55
- //
56
- // 为了兼容之前的写法,在10的倍数上以 16px 作为基准单位计算
57
- // 默认是以 4的倍数作为基数计算间距,但是由于 html font-size 变为了 10px
58
- // 现在版本从之前的16px -> 10px 整体缩小,所以,这里需要手动将间距放大,去适应以前的环境
59
- // TODO: 后期将所有间距调整为基准值, 不使用放大值
60
-
61
- /**
62
- * 因为之前版本 root size 为默认 16px, tailwind 默认就以 16px 为基准设置 spacing
63
- *
64
- * 而现在版本 root size 为 10px, 为了兼容以前代码, 将所有 spacing 重写
65
- *
66
- * @see https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale
67
- *
68
- * 例如:
69
- * 以前版本 1 -> 4px -> 0.25rem, 而现在 1 -> 4px -> 0.1rem
70
- * 以前版本 4 -> 16px -> 1rem 而现在 4 -> 16px -> 1.6rem
71
- *
72
- * 注意只是 spaceing 使用这规则,也就是将默认的 px 间距用 10 重新换算一遍,间距并未改变
73
- */
74
- spacing: {
75
- 0.5: '0.2rem',
76
- 1: '0.4rem',
77
- 1.5: '0.6rem',
78
- 2: '0.8rem',
79
- 2.5: '1rem',
80
- 3: '1.2rem',
81
- 3.5: '1.4rem',
82
- 4: '1.6rem',
83
- 5: '2rem',
84
- 6: '2.4rem',
85
- 7: '2.8rem',
86
- 8: '3.2rem',
87
- 9: '3.6rem',
88
- 10: '4rem',
89
- 11: '4.4rem',
90
- 12: '4.8rem',
91
- 14: '5.6rem',
92
- 16: '6.4rem',
93
- 20: '8rem',
94
- 24: '9.6rem',
95
- 28: '11.2rem',
96
- 32: '12.8rem',
97
- 36: '14.4rem',
98
- 40: '16rem',
99
- 44: '17.6rem',
100
- 48: '19.2rem',
101
- 52: '20.8rem',
102
- 56: '22.4rem',
103
- 60: '24rem',
104
- 64: '25.6rem',
105
- 72: '28.8rem',
106
- 80: '32rem',
107
- 96: '38.4rem'
108
- },
109
-
110
- /**
111
- * 字体对应 font-size 和 line-height
112
- *
113
- * @see https://tailwindcss.com/docs/font-size
114
- */
115
- fontSize: {
116
- xxs: ['2rem', '1.2rem'],
117
- xs: ['1.2rem', '1.6rem'],
118
- sm: ['1.4rem', '2rem'],
119
- base: ['1.6rem', '2.4rem'],
120
- lg: ['1.8rem', '2.8rem'],
121
- xl: ['2rem', '2.8rem'],
122
- '2xl': ['2.4rem', '3.2rem'],
123
- '3xl': ['3rem', '3.6rem'],
124
- '4xl': ['3.6rem', '4rem'],
125
- '5xl': ['4.8rem', 1],
126
- '6xl': ['6rem', 1],
127
- '7xl': ['7.2rem', 1],
128
- '8xl': ['9.6rem', 1],
129
- '9xl': ['12.8rem', 1],
130
- 12: ['1.2rem'],
131
- 14: ['1.4rem'],
132
- 16: ['1.6rem'],
133
- 18: ['1.8rem'],
134
- 20: ['2rem'],
135
- 22: ['2.2rem'],
136
- 24: ['2.4rem'],
137
- 26: ['2.6rem'],
138
- 32: ['3.2rem'],
139
- 34: ['3.4rem'],
140
- 36: ['3.6rem'],
141
- 40: ['4rem'],
142
- 46: ['4.6rem'],
143
- 50: ['5rem'],
144
- 54: ['5.4rem'],
145
- 60: ['6rem'],
146
- 70: ['7rem'],
147
- 80: ['8rem'],
148
- 90: ['9rem'],
149
- 100: ['10rem']
150
- },
151
-
152
- screens: {
153
- /** @media (min-width: 375px) { ... } */
154
- xs: '375px',
155
- /** @media (min-width: 640px) { ... } */
156
- sm: '640px',
157
- /** @media (min-width: 768px) { ... } */
158
- md: '768px',
159
- /** @media (min-width: 1024px) { ... } */
160
- lg: '1024px',
161
- /** @media (min-width: 1280px) { ... } */
162
- xl: '1280px',
163
- /** @media (min-width: 1536px) { ... } */
164
- '2xl': '1536px',
165
- /** @media (min-width: 1640px) { ... } */
166
- '3xl': '1640px',
167
- /** @media (min-width: 1920px) { ... } */
168
- maxxl: '1920px'
169
- }
170
- };
171
-
172
- const root10pxPlugin = plugin(({ addBase }) => {
173
- addBase({
174
- html: { fontSize: '10px' }
175
- });
176
- });
177
-
178
- export default { themes: root10pxThemes, plugin: root10pxPlugin };
@@ -1,238 +0,0 @@
1
- import plugin from 'tailwindcss/plugin';
2
- import template from 'lodash/template';
3
- import isPlainObject from 'lodash/isPlainObject';
4
- import isString from 'lodash/isString';
5
-
6
- class KeyTemplate {
7
- constructor(options) {
8
- /**
9
- * @type {import('./type').ThemeConfig}
10
- */
11
- this.options = options;
12
- }
13
-
14
- /**
15
- * Get the colors value
16
- *
17
- * @example
18
- * ```
19
- * colors: {
20
- * primary: {
21
- * "500": getColorsValue()
22
- * },
23
- * secondary: getColorsValue()
24
- * }
25
- * ```
26
- *
27
- * @param {string} colorKey - The color key
28
- * @param {string} value - The color value
29
- * @returns {string} - The colors value
30
- */
31
- getColorsValue({ key, parentKey = '', value }) {
32
- const { colorsValueTemplate, ...rest } = this.options;
33
-
34
- let colorKey = key;
35
- if (parentKey) {
36
- colorKey = this.composeKey(key, parentKey);
37
- }
38
-
39
- // if colorsValueTemplate is provided, use it to generate the colors value
40
- if (colorsValueTemplate) {
41
- const styleKey = this.getStyleKey(colorKey);
42
- return template(colorsValueTemplate)({
43
- ...rest,
44
- styleKey,
45
- key,
46
- parentKey,
47
- value
48
- });
49
- }
50
-
51
- return value;
52
- }
53
-
54
- /**
55
- * Get the style theme key
56
- *
57
- * ```
58
- * <style>
59
- * [getStyleThemeKey()] {
60
- * primary: #000;
61
- * }
62
- * </style>
63
- * ```
64
- *
65
- * @param {string} theme - The theme name
66
- * @returns {string} - The style theme key
67
- */
68
- getStyleThemeKey(theme) {
69
- const { styleThemeKeyTemplate, target, ...rest } = this.options;
70
-
71
- // if styleThemeKeyTemplate is provided, use it to generate the style theme key
72
- if (styleThemeKeyTemplate) {
73
- return template(styleThemeKeyTemplate)({ ...rest, theme, target });
74
- }
75
-
76
- return `${target}.${theme}`;
77
- }
78
-
79
- /**
80
- * Get the style key template
81
- *
82
- * @example
83
- *
84
- * ```
85
- * <style>
86
- * [getStyleThemeKey()] {
87
- * [getStyleKeyTemplate()]: #000;
88
- * }
89
- * </style>
90
- * ```
91
- *
92
- * @param {string} key - The key name
93
- * @param {string | undefined} parentKey - The parent key name
94
- * @returns {string} - The style key template
95
- */
96
- getStyleKey(key, parentKey) {
97
- const { styleKeyTemplate, ...rest } = this.options;
98
-
99
- // if parentKey is provided, compose the key
100
- let colorKey = key;
101
- if (parentKey) {
102
- colorKey = this.composeKey(key, parentKey);
103
- }
104
-
105
- // if styleKeyTemplate is provided, use it to generate the style key
106
- if (styleKeyTemplate) {
107
- return template(styleKeyTemplate)({ ...rest, colorKey, parentKey });
108
- }
109
-
110
- return key;
111
- }
112
-
113
- composeKey(key, parentKey) {
114
- return parentKey ? `${parentKey}-${key}` : key;
115
- }
116
- }
117
-
118
- class Generator {
119
- constructor(options) {
120
- this.options = options;
121
- this.keyTemplate = new KeyTemplate(options);
122
- }
123
-
124
- generateBaseStyles() {
125
- const { colors } = this.options;
126
-
127
- const baseStyles = {};
128
-
129
- Object.entries(colors).forEach(([theme, themeColors]) => {
130
- const styleThemeKey = this.keyTemplate.getStyleThemeKey(theme);
131
-
132
- baseStyles[styleThemeKey] = {};
133
-
134
- Object.entries(themeColors).forEach(([colorKey, value]) => {
135
- if (isPlainObject(value)) {
136
- Object.entries(value).forEach(([key, colorValue]) => {
137
- baseStyles[styleThemeKey][
138
- this.keyTemplate.getStyleKey(key, colorKey)
139
- ] = colorValue;
140
- });
141
- }
142
- // if value is string, it means it's a color value
143
- else if (isString(value)) {
144
- baseStyles[styleThemeKey][this.keyTemplate.getStyleKey(colorKey)] =
145
- value;
146
- }
147
- });
148
- });
149
-
150
- return this.setDefaultStyleThemeKey(baseStyles);
151
- }
152
-
153
- setDefaultStyleThemeKey(baseStyles) {
154
- const { defaultTheme, colors } = this.options;
155
-
156
- // set default style theme key
157
- let _defaultTheme = defaultTheme;
158
- if (
159
- // if default theme is not provided, set it to the first theme
160
- !defaultTheme ||
161
- // if default theme is system, set it to the first theme
162
- defaultTheme === 'system' ||
163
- // if default theme is not in colors, set it to the first theme
164
- !Object.keys(colors).includes(defaultTheme)
165
- ) {
166
- _defaultTheme = Object.keys(colors)?.[0];
167
- }
168
-
169
- const defaultStyleThemeKey =
170
- this.keyTemplate.getStyleThemeKey(_defaultTheme);
171
-
172
- const result = {};
173
- Object.entries(baseStyles).forEach(([key, value]) => {
174
- if (key === defaultStyleThemeKey) {
175
- result[':root'] = value;
176
- } else {
177
- result[key] = value;
178
- }
179
- });
180
-
181
- return result;
182
- }
183
-
184
- generateThemeColors() {
185
- const { colors } = this.options;
186
-
187
- const themeResultColors = {};
188
-
189
- Object.entries(colors).forEach(([, themeColors]) => {
190
- Object.entries(themeColors).forEach(([colorKey, value]) => {
191
- themeResultColors[colorKey] = {};
192
-
193
- if (isPlainObject(value)) {
194
- Object.entries(value).forEach(([key, colorValue]) => {
195
- const numberKey = Number(key);
196
- themeResultColors[colorKey][numberKey] =
197
- this.keyTemplate.getColorsValue({
198
- key,
199
- parentKey: colorKey,
200
- value: colorValue
201
- });
202
- });
203
- }
204
- // if value is string, it means it's a color value
205
- else if (isString(value)) {
206
- themeResultColors[colorKey] = this.keyTemplate.getColorsValue({
207
- key: colorKey,
208
- value
209
- });
210
- }
211
- });
212
- });
213
-
214
- return themeResultColors;
215
- }
216
- }
217
-
218
- function create(options) {
219
- const generator = new Generator(options);
220
-
221
- const baseStyles = generator.generateBaseStyles();
222
- const themeColors = generator.generateThemeColors();
223
-
224
- const result = {
225
- baseStyles,
226
- colors: themeColors
227
- };
228
-
229
- if (Object.keys(baseStyles).length > 0) {
230
- result.plugin = plugin(({ addBase }) => {
231
- addBase(baseStyles);
232
- });
233
- }
234
-
235
- return result;
236
- }
237
-
238
- export default create;
@@ -1,3 +0,0 @@
1
- {
2
- "title": "About"
3
- }
@@ -1,6 +0,0 @@
1
- {
2
- "executorDemo": "Executor Demo",
3
- "requestTimeout": "Request Timeout",
4
- "executorTestPlugin": "Executor Test Plugin",
5
- "testPlugin": "Test Plugin"
6
- }
@@ -1,10 +0,0 @@
1
- {
2
- "welcome": "Welcome to the home page",
3
- "description": "A modern frontend utility library collection providing various practical tools and components",
4
- "about": "About Us",
5
- "about_description": "Learn more about our project and team information",
6
- "jsonstorage": "JSONStorage",
7
- "jsonstorage_description": "Efficient JSON data storage solution",
8
- "request": "Request",
9
- "request_description": "A modern frontend utility library collection providing various practical tools and components"
10
- }
@@ -1,11 +0,0 @@
1
- {
2
- "title": "JSONStorage Demo",
3
- "title2": "Permanent storage test",
4
- "title3": "Expire time test",
5
- "title4": "Request Timeout",
6
- "format.title": "Test key: ${key}, Random value range: ${min}~${max}",
7
- "setRandomValue": "Set random value",
8
- "currentValue": "Current value",
9
- "ms": "ms",
10
- "setExpireTime": "Set random value(with expire time)"
11
- }
@@ -1,7 +0,0 @@
1
- {
2
- "title": "Login",
3
- "email": "Email",
4
- "username": "Username",
5
- "password": "Password",
6
- "login": "Login"
7
- }
@@ -1,15 +0,0 @@
1
- {
2
- "requestTimeout": "Request Timeout",
3
- "helloResult": "Hello result is",
4
- "helloError": "Hello error is",
5
- "ipInfoResult": "IpInfo result is",
6
- "ipInfo": "IpInfo",
7
- "randomUser": "RandomUser",
8
- "loading": "Loading...",
9
- "randomUserResult": "RandomUser result is",
10
- "randomUserError": "RandomUser error is",
11
- "triggerAbortRequest": "Trigger Abort Request",
12
- "stopAbortRequest": "Stop Abort Request",
13
- "abortRequestResult": "Abort Request Result",
14
- "abortRequestError": "Abort Request Error"
15
- }
@@ -1,3 +0,0 @@
1
- {
2
- "title": "关于我们"
3
- }
@@ -1,7 +0,0 @@
1
- {
2
- "executorDemo": "执行器 Demo",
3
- "requestTimeout": "请求超时",
4
- "executorTestPlugin": "执行器 测试插件",
5
- "testPlugin": "测试插件"
6
- }
7
-
@@ -1,10 +0,0 @@
1
- {
2
- "welcome": "欢迎来到主页",
3
- "description": "一个现代前端实用库集合,提供各种实用工具和组件",
4
- "about": "关于我们",
5
- "about_description": "了解更多关于我们的项目和团队信息",
6
- "jsonstorage": "JSON存储",
7
- "jsonstorage_description": "高效的JSON数据存储解决方案",
8
- "request": "请求",
9
- "request_description": "一个现代前端实用库集合,提供各种实用工具和组件"
10
- }
@@ -1,11 +0,0 @@
1
- {
2
- "title": "JSONStorage Demo",
3
- "title2": "永久存储测试",
4
- "title3": "过期时间测试",
5
- "title4": "请求超时时间设置",
6
- "format.title": "测试 key: ${key}, 随机值范围: ${min}~${max}",
7
- "setRandomValue": "设置随机值",
8
- "currentValue": "当前值",
9
- "ms": "毫秒",
10
- "setExpireTime": "设置随机值(带过期时间)"
11
- }
@@ -1,8 +0,0 @@
1
- {
2
- "title": "登录",
3
- "email": "邮箱",
4
- "username": "用户名",
5
- "password": "密码",
6
- "login": "登录"
7
- }
8
-
@@ -1,15 +0,0 @@
1
- {
2
- "requestTimeout": "请求超时",
3
- "helloResult": "Hello 结果是",
4
- "helloError": "Hello 错误是",
5
- "ipInfoResult": "IpInfo 结果是",
6
- "ipInfo": "IpInfo",
7
- "randomUser": "随机用户",
8
- "loading": "加载中...",
9
- "randomUserResult": "随机用户 结果是",
10
- "randomUserError": "随机用户 错误是",
11
- "triggerAbortRequest": "触发中止请求",
12
- "stopAbortRequest": "停止中止请求",
13
- "abortRequestResult": "中止请求 结果是",
14
- "abortRequestError": "中止请求 错误是"
15
- }
@@ -1,9 +0,0 @@
1
- import type { IOCRegisterInterface } from '@qlover/corekit-bridge';
2
- import type { Container } from 'inversify';
3
-
4
- export type InversifyRegisterContainer = Container;
5
- /**
6
- * Inversify register interface.
7
- */
8
- export interface InversifyRegisterInterface
9
- extends IOCRegisterInterface<InversifyRegisterContainer> {}
@@ -1,3 +0,0 @@
1
- html {
2
- @apply bg-white;
3
- }
@@ -1,3 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;