@component-hook/create-app 1.1.0 → 1.1.1

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 (33) hide show
  1. package/README.md +61 -37
  2. package/dist/index.js +35 -18
  3. package/package.json +1 -1
  4. package/template-vue/README.md.art +31 -50
  5. package/template-vue/index.html.art +2 -0
  6. package/template-vue/lint-staged.config.js +2 -0
  7. package/template-vue/package.json +27 -29
  8. package/template-vue/playwright.config.ts +12 -23
  9. package/template-vue/postcss.config.js +11 -10
  10. package/template-vue/src/__tests__/e2e/{IndexPage.spec.ts → home.spec.ts} +8 -4
  11. package/template-vue/src/__tests__/unit/components/{indexPage → home}/HelloWorld.test.ts +1 -1
  12. package/template-vue/src/components/svg-icon/index.ts +1 -0
  13. package/template-vue/src/main.ts +2 -3
  14. package/template-vue/src/{components/indexPage → pages/home/components}/HelloWorld.vue +3 -8
  15. package/template-vue/src/pages/{IndexPage.vue → home/index.vue} +14 -3
  16. package/template-vue/src/router/index.ts +2 -2
  17. package/template-vue/src/stores/modules/{config.ts → config.ts.art} +1 -1
  18. package/template-vue/src/styles/base.css +48 -0
  19. package/template-vue/src/styles/common/all.css +1 -0
  20. package/template-vue/src/styles/common/button.css +15 -0
  21. package/template-vue/src/styles/index.css +3 -0
  22. package/template-vue/src/styles/tailwind.css +1 -0
  23. package/template-vue/src/vite-env.d.ts +1 -0
  24. package/template-vue/vite.config.ts +8 -7
  25. package/template-vue/public/mockServiceWorker.js +0 -307
  26. package/template-vue/src/components.d.ts +0 -14
  27. package/template-vue/src/styles/common/all.postcss +0 -1
  28. package/template-vue/src/styles/common/button.postcss +0 -12
  29. package/template-vue/src/styles/index.postcss +0 -1
  30. package/template-vue/src/styles/tailwind.postcss +0 -39
  31. package/template-vue/tailwind.config.js +0 -20
  32. /package/template-vue/src/assets/images/{svgIcons → svg-icons}/vue.svg +0 -0
  33. /package/template-vue/src/components/{common/SvgIcon.vue → svg-icon/src/index.vue} +0 -0
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Scaffolding a basic `typescript` project.
4
4
 
5
5
  > **Compatibility Note:**
6
- > Requires [Node.js](https://nodejs.org/en/) version 18+, 20+. However, some templates require a higher Node.js version to work, please upgrade if your package manager warns about it.
6
+ > Requires [Node.js](https://nodejs.org/en/) version 20+. However, some templates require a higher Node.js version to work, please upgrade if your package manager warns about it.
7
7
 
8
8
  <p>
9
9
  <a href="https://npm-stat.com/charts.html?package=@component-hook/create-app">
@@ -44,39 +44,63 @@ Currently supported template presets include:
44
44
 
45
45
  - `vue`
46
46
 
47
- ## Project Default Setup
48
-
49
- - [Vite](https://cn.vite.dev/)
50
- - [TypeScript](https://www.typescriptlang.org/)
51
- - [Axios](https://github.com/axios/axios)
52
- - [TailwindcSS](https://tailwindcss.com/)
53
- - [PostCSS](https://postcss.org/) w/ `postcss-nesting` plugin
54
- - [Cssnano](https://cssnano.github.io/cssnano/) for minimizing production CSS
55
- - [Postcss-pxtorem](https://www.npmjs.com/package/postcss-pxtorem) generates rem units from pixel units
56
- - [Vite-plugin-svg-icons](https://github.com/vbenjs/vite-plugin-svg-icons) generate svg sprite map
57
- - [Eslint](https://eslint.org/)
58
- - [@component-hook/eslint-plugin](https://www.npmjs.com/package/@component-hook/eslint-plugin?activeTab=readme)
59
- - [Prettier](https://prettier.io/)
60
- - [Husky](https://www.npmjs.com/package/husky) to improves commits
61
- - if encounter npx: command not found, can execute `ln -s $(which npx) /usr/local/bin/npx` in zsh
62
- - [commitlint](https://commitlint.js.org/#/) checks if your commit messages meet the conventional commit format
63
- - [lint-staged](https://github.com/okonet/lint-staged) filter files checked by pre commit lint
64
- - Alias `@` to `<project_root>/src`
65
- - Mock use [msw](https://mswjs.io/)
66
- - `npx msw init ./public`
67
- - Unit test use [Vitest](https://vitest.dev/) + [Testing Library](https://testing-library.com/)
68
- - [jsdom](https://www.npmjs.com/package/jsdom)
69
- - [@vitest/coverage-v8](https://vitest.dev/guide/coverage.html)
70
- - [@testing-library/jest-dom](https://testing-library.com/docs/ecosystem-jest-dom/)
71
- - [@testing-library/user-event](https://testing-library.com/docs/ecosystem-user-event/)
72
- - E2E test use [playwright](https://playwright.dev/)
73
- - [Rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer) visualize and analyze your Rollup bundle to see which modules are taking up space
74
- - Predefined and fully typed global variables:
75
- - `VITE_APP_VERSION` is read from `package.json` version at build time
76
- - `VITE_APP_BUILD_EPOCH` is populated as `new Date().getTime()` at build time
77
- - Github workflows (default)
78
- - ci - on workflow call and dispatch
79
- - deploy - on push main branch
80
- - preview - on pull request
81
- - Gitlab CI (use `--gitlab` command create project)
82
- - run tests - on branches, merge request and manual
47
+ ## Project Features Overview
48
+
49
+ This project leverages modern frontend practices and tooling, covering development experience, code quality, testing, and deployment.
50
+
51
+ ### Development Experience
52
+
53
+ - **Written in TypeScript**: Provides type safety and better maintainability.
54
+ - **Styled with Tailwind CSS**: Utility-first CSS framework for fast and consistent UI styling.
55
+ - **SVG Support**: Integrated [`vite-plugin-svg-icons`](https://github.com/vbenjs/vite-plugin-svg-icons) to use SVGs as Vue components.
56
+
57
+ ### API & Mocking
58
+
59
+ - Handles HTTP requests using `axios`.
60
+ - Uses `msw` (Mock Service Worker) for mocking APIs, enabling smooth local development and testing.
61
+
62
+ ### Code Quality
63
+
64
+ - Enforces consistent code style with `ESLint` and `Prettier`.
65
+ - Enhanced with:
66
+ - `husky`: For Git hooks.
67
+ - `lint-staged`: Only runs linters on staged files.
68
+ - `commitlint`: Ensures commit messages follow conventional format.
69
+
70
+ ### Testing
71
+
72
+ - Unit testing with `Vitest` + `Testing Library`.
73
+ - End-to-end (E2E) testing using `Playwright`.
74
+
75
+ ### Optimization
76
+
77
+ - Minifies CSS with `cssnano`.
78
+ - Converts px to rem using `postcss-pxtorem` for responsive design.
79
+ - Uses `rollup-plugin-visualizer` to analyze bundle sizes.
80
+
81
+ ### CI / CD
82
+
83
+ - Integrated `GitHub Actions` and `GitLab CI` for automated testing and deployment.
84
+
85
+ ### Project Structure & Conventions
86
+
87
+ - The project root alias `@` points to the `src` directory (`<project_root>/src`).
88
+ - ESLint uses a custom plugin: [`@component-hook/eslint-plugin`](https://www.npmjs.com/package/@component-hook/eslint-plugin).
89
+
90
+ ### Tip: Solving `npx: command not found`
91
+
92
+ If you encounter `npx: command not found`, run the following command to create a symbolic link:
93
+
94
+ ```bash
95
+ ln -s $(which npx) /usr/local/bin/npx
96
+ ```
97
+
98
+ ### Predefined Global Variables
99
+
100
+ These variables are injected at build time and are fully typed:
101
+
102
+ | Variable Name | Description |
103
+ | ---------------------- | -------------------------------------------- |
104
+ | `VITE_APP_VERSION` | Read from `package.json` version |
105
+ | `VITE_APP_BUILD_EPOCH` | Timestamp generated at build time |
106
+ | `VITE_APP_MOCK` | Indicates whether the mock environment is on |
package/dist/index.js CHANGED
@@ -1,28 +1,29 @@
1
1
  // src/index.ts
2
+ import { readdirSync as readdirSync2, statSync } from "node:fs";
3
+ import { readdir, readFile, writeFile } from "node:fs/promises";
2
4
  import path2 from "node:path";
3
5
  import { fileURLToPath } from "node:url";
4
- import { readdir, readFile, writeFile } from "node:fs/promises";
5
- import minimist from "minimist";
6
- import colors2 from "picocolors";
7
- import { copy, ensureDirSync } from "fs-extra/esm";
8
6
  import artTemplate from "art-template";
7
+ import { copy, ensureDirSync } from "fs-extra/esm";
8
+ import minimist from "minimist";
9
9
  import ora from "ora";
10
+ import colors2 from "picocolors";
11
+
12
+ // src/constants.ts
13
+ var DEFAULT_PROJECT_NAME = "basic-project";
14
+ var TEMPLATES = ["vue"];
15
+ var GITHUB_ACTIONS = "GitHub Actions";
16
+ var GITLAB_CI = "GitLab CI";
10
17
 
11
18
  // src/prompts.ts
12
19
  import { existsSync as existsSync2 } from "node:fs";
13
- import prompts from "prompts";
14
20
  import colors from "picocolors";
21
+ import prompts from "prompts";
15
22
 
16
23
  // src/utils.ts
17
- import path from "node:path";
18
24
  import { existsSync, readdirSync } from "node:fs";
25
+ import path from "node:path";
19
26
  import { remove } from "fs-extra/esm";
20
-
21
- // src/config.ts
22
- var DEFAULT_PROJECT_NAME = "basic-project";
23
- var TEMPLATES = ["vue"];
24
-
25
- // src/utils.ts
26
27
  function formatTargetDir(targetDir) {
27
28
  if (!targetDir) return targetDir;
28
29
  targetDir = targetDir.trim();
@@ -46,6 +47,9 @@ function isValidPackageName(projectName) {
46
47
  function toValidPackageName(projectName) {
47
48
  return projectName.trim().toLowerCase().replaceAll(/\s+/g, "-").replace(/^[._]/, "").replaceAll(/[^a-z\d\-~]+/g, "-");
48
49
  }
50
+ function toUpperCasePackageName(projectName) {
51
+ return projectName.split("-").map((str) => `${str[0].toUpperCase()}${str.slice(1)}`).join(" ");
52
+ }
49
53
  function getPkgManagerInfo() {
50
54
  const userAgent = process.env.npm_config_user_agent;
51
55
  if (!userAgent) return;
@@ -108,7 +112,7 @@ function operationPrompts({ targetDir, template }) {
108
112
  type: (_, { projectName: nameAnswer }) => {
109
113
  return isValidPackageName(getProjectName(nameAnswer)) ? null : "text";
110
114
  },
111
- name: "projectName",
115
+ name: "packageName",
112
116
  message: styleTitle("Package name:"),
113
117
  initial: (_, { projectName: nameAnswer }) => toValidPackageName(getProjectName(nameAnswer)),
114
118
  validate: (name) => {
@@ -154,7 +158,7 @@ Available templates:
154
158
  ${colors2.green("vue")}
155
159
  `;
156
160
  async function copyTemplateFolder(root, templateDir, packageName) {
157
- const isGitlab = argv.lab || argv.gitlab;
161
+ const isGitlab = argv.l || argv.gitlab;
158
162
  const [files, pkgJson] = await Promise.all([
159
163
  readdir(templateDir),
160
164
  readFile(path2.join(templateDir, "package.json"), "utf-8")
@@ -172,11 +176,24 @@ async function copyTemplateFolder(root, templateDir, packageName) {
172
176
  }
173
177
  const templatePath = path2.join(templateDir, file);
174
178
  if (file.endsWith(".art")) {
175
- const name = packageName.split("-").map((str) => `${str[0].toUpperCase()}${str.slice(1)}`).join(" ");
176
- const renderedResult = artTemplate(templatePath, { projectName: name });
177
- return writeFile(targetPath.replace(".art", ""), renderedResult);
179
+ const isScript = file.endsWith(".js.art") || file.endsWith(".ts.art");
180
+ const name = isScript ? packageName : toUpperCasePackageName(packageName);
181
+ const replacedPath = targetPath.replace(".art", "");
182
+ const renderedResult = artTemplate(templatePath, {
183
+ projectName: name,
184
+ gitRepository: isGitlab ? GITLAB_CI : GITHUB_ACTIONS
185
+ });
186
+ ensureDirSync(path2.dirname(replacedPath));
187
+ return writeFile(replacedPath, renderedResult);
188
+ }
189
+ if (statSync(templatePath).isDirectory()) {
190
+ const subFiles = readdirSync2(templatePath);
191
+ const rewriteOrCopySubFilePromises = subFiles.map((subFile) => rewriteOrCopyFile(path2.join(file, subFile)));
192
+ return Promise.all(rewriteOrCopySubFilePromises).then(() => {
193
+ });
194
+ } else {
195
+ return copy(templatePath, targetPath);
178
196
  }
179
- return copy(templatePath, targetPath);
180
197
  };
181
198
  pkg.name = packageName;
182
199
  return Promise.all([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@component-hook/create-app",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Scaffolding a basic typescript project",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,49 +1,26 @@
1
1
  # {{projectName}}
2
2
 
3
+ This project is built with [vue3](https://vuejs.org/) + [vite](https://vite.dev/), using [pnpm](https://pnpm.io/) for package management and [pinia](https://pinia.vuejs.org/) for state management.
4
+
3
5
  ## Features
4
6
 
5
- - Vue 3
6
- - Pnpm 9
7
- - Vite 5
8
- - [Pinia](https://pinia.vuejs.org/) store
9
- - [pinia-plugin-persistedstate](https://www.npmjs.com/package/pinia-plugin-persistedstate)
10
- - Routing using [vue-router 4](https://router.vuejs.org/)
11
- - TypeScript 5
12
- - Axios
13
- - Tailwind CSS 3
14
- - Vue-i18n 9
15
- - PostCSS 8 w/ `postcss-nesting` plugin
16
- - [Cssnano](https://cssnano.github.io/cssnano/) for minimizing production CSS
17
- - [Postcss-pxtorem](https://www.npmjs.com/package/postcss-pxtorem) generates rem units from pixel units
18
- - [Vite-plugin-svg-icons](https://github.com/vbenjs/vite-plugin-svg-icons) generate svg sprite map
19
- - [Unplugin-vue-components](https://www.npmjs.com/package/unplugin-vue-components) auto importing for common components
20
- - Eslint
21
- - [@component-hook/eslint-plugin](https://www.npmjs.com/package/@component-hook/eslint-plugin?activeTab=readme)
22
- - Prettier
23
- - [Husky](https://www.npmjs.com/package/husky) to improves commits
24
- - if encounter npx: command not found, can execute `ln -s $(which npx) /usr/local/bin/npx` in zsh
25
- - [commitlint](https://commitlint.js.org/#/) checks if your commit messages meet the conventional commit format
26
- - [lint-staged](https://github.com/okonet/lint-staged) filter files checked by pre commit lint
27
- - Alias `@` to `<project_root>/src`
28
- - Mock use [msw](https://mswjs.io/)
29
- - `npx msw init ./public`
30
- - Unit test use [Vitest](https://vitest.dev/) + [Testing Library](https://testing-library.com/)
31
- - [jsdom](https://www.npmjs.com/package/jsdom)
32
- - [@vitest/coverage-v8](https://vitest.dev/guide/coverage.html)
33
- - [@testing-library/jest-dom](https://testing-library.com/docs/ecosystem-jest-dom/)
34
- - [@testing-library/vue](https://testing-library.com/docs/vue-testing-library/intro/)
35
- - [@testing-library/user-event](https://testing-library.com/docs/ecosystem-user-event/)
36
- - E2E test use [playwright](https://playwright.dev/)
37
- - [Rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer) visualize and analyze your Rollup bundle to see which modules are taking up space
7
+ - **Development Experience**: Written in [typeScript](https://www.typescriptlang.org/), styled with [tailwindcss](https://tailwindcss.com/), and structured with [vue-router](https://router.vuejs.org/), and [vue-i18n](https://vue-i18n.intlify.dev/). Supports SVGs ([vite-plugin-svg-icons](https://github.com/vbenjs/vite-plugin-svg-icons)).
8
+ - **API & Mocking**: Handles requests with [axios](https://axios-http.com/docs/intro) and mocks APIs using [msw](https://mswjs.io/).
9
+ - **Code Quality**: Enforces standards with [eslint](https://eslint.org/) and [prettier](https://prettier.io/), with [husky](https://www.npmjs.com/package/husky), [commitlint](https://commitlint.js.org/#/), and [lint-staged](https://github.com/okonet/lint-staged) for commit validation.
10
+ - **Testing**: Uses [vitest](https://vitest.dev/) + [testing-library](https://testing-library.com/) for unit testing and [playwright](https://playwright.dev/) for E2E testing.
11
+ - **Optimization**: Compresses CSS with [cssnano](https://cssnano.github.io/cssnano/) , converts units with [postcss-pxtorem](https://www.npmjs.com/package/postcss-pxtorem), and analyzes assets with [rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer).
12
+ - **CI/CD**: Integrates `{{gitRepository}}` for automated testing and deployment.
13
+
14
+ ## Code Features / Opinions
15
+
16
+ - Project root aliased as `@` to `<project_root>/src`
17
+ - Store persistedstate use [pinia-plugin-persistedstate](https://www.npmjs.com/package/pinia-plugin-persistedstate)
18
+ - ESlint plugin use [@component-hook/eslint-plugin](https://www.npmjs.com/package/@component-hook/eslint-plugin?activeTab=readme)
19
+ - if encounter npx: command not found, can execute `ln -s $(which npx) /usr/local/bin/npx` in zsh
38
20
  - Predefined and fully typed global variables:
39
21
  - `VITE_APP_VERSION` is read from `package.json` version at build time
40
22
  - `VITE_APP_BUILD_EPOCH` is populated as `new Date().getTime()` at build time
41
- - Github workflows
42
- - ci - on workflow call and dispatch
43
- - deploy - on push main branch
44
- - preview - on pull request
45
- - Gitlab CI
46
- - run tests - on branches, merge request and manual
23
+ - `VITE_APP_MOCK` is use mock environment
47
24
 
48
25
  ## Project Files
49
26
 
@@ -58,14 +35,14 @@ src/
58
35
  ├── apis/*
59
36
  ├── assets/*
60
37
  │ └── images/
61
- │ └── svgIcons
38
+ │ └── svg-icons
62
39
  │ └── vue.svg
63
40
  ├── components/
64
- │ ├── common/
65
- │ │ └── SvgIcon.vue
66
- │ └── indexPage/
67
- │ └── HelloWorld.vue
68
- ├── configs/*
41
+ │ └── svg-icon/
42
+ │ ├── src
43
+ │ │ └── index.vue
44
+ │ └── index.ts
45
+ ├── constants/*
69
46
  ├── hooks/*
70
47
  ├── locales/
71
48
  │ ├── en-US.json
@@ -77,7 +54,10 @@ src/
77
54
  │ │── browser.ts
78
55
  │ └── server.ts
79
56
  ├── pages/
80
- │ └── IndexPage.vue
57
+ │ └── home/
58
+ │ ├── components
59
+ │ │ └── HelloWorld.vue
60
+ │ └── index.vue
81
61
  ├── plugins/
82
62
  │ └── i18n.ts
83
63
  ├── router/
@@ -88,10 +68,11 @@ src/
88
68
  │ └── index.ts
89
69
  ├── styles/
90
70
  │ ├── common/
91
- | │ ├── all.postcss
92
- | │ └── button.postcss
93
- │ ├── index.postcss
94
- │ └── tailwind.postcss
71
+ | │ ├── all.css
72
+ | │ └── button.css
73
+ │ ├── base.css
74
+ │ ├── index.css
75
+ │ └── tailwind.css
95
76
  ├── types/*
96
77
  ├── utils/*
97
78
  ├── App.vue
@@ -20,6 +20,8 @@
20
20
  src="/src/main.ts"
21
21
  ></script>
22
22
  <noscript>
23
+ <h1>Opinionated, production ready template for Vite and Vue 3</h1>
24
+
23
25
  <p>This site requires JavaScript to work.</p>
24
26
  </noscript>
25
27
  </body>
@@ -1,3 +1,5 @@
1
+ /** @type {import('lint-staged').Configuration} */
2
+
1
3
  export default {
2
4
  '*.{ts,vue}': () => 'pnpm typecheck',
3
5
  '*.{ts,js,vue}': () => 'pnpm lint',
@@ -3,7 +3,7 @@
3
3
  "version": "0.0.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "packageManager": "pnpm@10.6.3",
6
+ "packageManager": "pnpm@10.10.0",
7
7
  "scripts": {
8
8
  "dev": "vite",
9
9
  "dev:mock": "MOCK=true vite",
@@ -18,43 +18,41 @@
18
18
  "preview": "vite preview"
19
19
  },
20
20
  "dependencies": {
21
- "axios": "^1.8.3",
22
- "pinia": "^3.0.1",
23
- "pinia-plugin-persistedstate": "^4.2.0",
21
+ "axios": "^1.9.0",
22
+ "pinia": "^3.0.2",
23
+ "pinia-plugin-persistedstate": "^4.3.0",
24
24
  "vue": "^3.5.13",
25
- "vue-i18n": "^11.1.2",
26
- "vue-router": "^4.5.0"
25
+ "vue-i18n": "^11.1.3",
26
+ "vue-router": "^4.5.1"
27
27
  },
28
28
  "devDependencies": {
29
- "@component-hook/eslint-plugin": "1.0.1",
30
- "@intlify/unplugin-vue-i18n": "^6.0.3",
31
- "@playwright/test": "^1.51.0",
29
+ "@component-hook/eslint-plugin": "1.1.0",
30
+ "@intlify/unplugin-vue-i18n": "^6.0.8",
31
+ "@playwright/test": "^1.52.0",
32
+ "@tailwindcss/vite": "^4.1.6",
32
33
  "@testing-library/jest-dom": "^6.6.3",
33
34
  "@testing-library/user-event": "^14.6.1",
34
35
  "@testing-library/vue": "^8.1.0",
35
- "@tsconfig/node22": "22.0.0",
36
- "@types/node": "^22.13.10",
37
- "@vitejs/plugin-vue": "^5.2.2",
38
- "@vitest/coverage-v8": "^2.1.9",
36
+ "@tsconfig/node22": "^22.0.1",
37
+ "@types/node": "^22.15.17",
38
+ "@vitejs/plugin-vue": "^5.2.4",
39
+ "@vitest/coverage-v8": "^3.1.3",
39
40
  "@vue/tsconfig": "^0.7.0",
40
41
  "autoprefixer": "^10.4.21",
41
- "cssnano": "^7.0.6",
42
- "eslint": "^9.22.0",
43
- "jsdom": "^26.0.0",
44
- "msw": "^2.7.3",
42
+ "cssnano": "^7.0.7",
43
+ "eslint": "^9.26.0",
44
+ "jsdom": "^26.1.0",
45
+ "msw": "^2.8.2",
45
46
  "postcss": "^8.5.3",
46
- "postcss-import": "^16.1.0",
47
- "postcss-nesting": "^13.0.1",
48
47
  "postcss-pxtorem": "^6.1.0",
49
48
  "prettier": "^3.5.3",
50
- "rollup-plugin-visualizer": "^5.14.0",
51
- "tailwindcss": "^3.4.17",
52
- "typescript": "^5.8.2",
53
- "unplugin-vue-components": "^0.28.0",
54
- "vite": "^5.4.14",
49
+ "rollup-plugin-visualizer": "^5.13.1",
50
+ "tailwindcss": "^4.1.6",
51
+ "typescript": "^5.8.3",
52
+ "vite": "^6.3.5",
55
53
  "vite-plugin-svg-icons": "^2.0.1",
56
- "vitest": "^2.1.9",
57
- "vue-tsc": "^2.2.8"
54
+ "vitest": "^3.1.3",
55
+ "vue-tsc": "^2.2.10"
58
56
  },
59
57
  "browserslist": [
60
58
  "> 1%",
@@ -65,9 +63,9 @@
65
63
  "workerDirectory": "public"
66
64
  },
67
65
  "optionalDependencies": {
68
- "@commitlint/cli": "^19.8.0",
69
- "@commitlint/config-conventional": "^19.8.0",
66
+ "@commitlint/cli": "^19.8.1",
67
+ "@commitlint/config-conventional": "^19.8.1",
70
68
  "husky": "^9.1.7",
71
- "lint-staged": "^15.5.0"
69
+ "lint-staged": "^16.0.0"
72
70
  }
73
71
  }
@@ -24,37 +24,26 @@ export default defineConfig({
24
24
  projects: [
25
25
  {
26
26
  name: 'chromium',
27
- use: { ...devices['Desktop Chrome'] },
27
+ use: {
28
+ browserName: 'chromium',
29
+ ...devices['Macbook Pro'],
30
+ },
28
31
  },
29
32
 
30
33
  {
31
34
  name: 'firefox',
32
- use: { ...devices['Desktop Firefox'] },
35
+ use: {
36
+ browserName: 'firefox',
37
+ ...devices['Macbook 11'],
38
+ },
33
39
  },
34
40
 
35
41
  {
36
42
  name: 'webkit',
37
- use: { ...devices['Desktop Safari'] },
43
+ use: {
44
+ browserName: 'webkit',
45
+ ...devices['iPhone SE'],
46
+ },
38
47
  },
39
-
40
- /* Test against mobile viewports. */
41
- // {
42
- // name: 'Mobile Chrome',
43
- // use: { ...devices['Pixel 5'] },
44
- // },
45
- // {
46
- // name: 'Mobile Safari',
47
- // use: { ...devices['iPhone 12'] },
48
- // },
49
-
50
- /* Test against branded browsers. */
51
- // {
52
- // name: 'Microsoft Edge',
53
- // use: { ...devices['Desktop Edge'], channel: 'msedge' },
54
- // },
55
- // {
56
- // name: 'Google Chrome',
57
- // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
58
- // },
59
48
  ],
60
49
  });
@@ -1,14 +1,15 @@
1
+ import autoprefixer from 'autoprefixer';
2
+ import cssnano from 'cssnano';
3
+ import postcssPxtorem from 'postcss-pxtorem';
4
+
1
5
  export default {
2
- plugins: {
3
- 'postcss-import': {},
4
- 'tailwindcss/nesting': {},
5
- 'postcss-nesting': {},
6
- tailwindcss: {},
7
- autoprefixer: {},
8
- 'postcss-pxtorem': {
6
+ plugins: [
7
+ autoprefixer(),
8
+ postcssPxtorem({
9
9
  rootValue: 16,
10
10
  propList: ['*'],
11
- },
12
- ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
13
- },
11
+ selectorBlackList: ['norem'],
12
+ }),
13
+ ...(process.env.NODE_ENV === 'production' ? [cssnano({ preset: 'default' })] : []),
14
+ ],
14
15
  };
@@ -7,14 +7,16 @@ test.beforeEach(async ({ page }) => {
7
7
  test('click Vite logo open Vite official website blank tab', async ({ page, context }) => {
8
8
  await page.getByRole('link', { name: 'Vite logo' }).click();
9
9
  const newPage = await context.waitForEvent('page');
10
- await newPage.waitForLoadState();
10
+
11
+ await newPage.waitForLoadState('load');
11
12
  await expect(newPage).toHaveTitle('Vite | Next Generation Frontend Tooling');
12
13
  });
13
14
 
14
15
  test('click Vue logo open Vue official website blank tab', async ({ page, context }) => {
15
16
  await page.getByTitle('#icon-vue').click();
16
17
  const newPage = await context.waitForEvent('page');
17
- await newPage.waitForLoadState();
18
+
19
+ await newPage.waitForLoadState('load');
18
20
  await expect(newPage).toHaveTitle('Vue.js - The Progressive JavaScript Framework | Vue.js');
19
21
  });
20
22
 
@@ -35,14 +37,16 @@ test('locale change interact', async ({ page }) => {
35
37
  test('click create-vue link open Vue quick start blank tab', async ({ page, context }) => {
36
38
  await page.getByRole('link', { name: 'create-vue' }).click();
37
39
  const newPage = await context.waitForEvent('page');
38
- await newPage.waitForLoadState();
40
+
41
+ await newPage.waitForLoadState('load');
39
42
  await expect(newPage).toHaveTitle('Quick Start | Vue.js');
40
43
  });
41
44
 
42
45
  test('click Volar link open language-tools github blank tab', async ({ page, context }) => {
43
46
  await page.getByRole('link', { name: 'Volar' }).click();
44
47
  const newPage = await context.waitForEvent('page');
45
- await newPage.waitForLoadState();
48
+
49
+ await newPage.waitForLoadState('load');
46
50
  await expect(newPage).toHaveTitle(
47
51
  'GitHub - vuejs/language-tools: ⚡ High-performance Vue language tooling based-on Volar.js',
48
52
  );
@@ -1,7 +1,7 @@
1
1
  import userEvent from '@testing-library/user-event';
2
2
  import { screen } from '@testing-library/vue';
3
3
  import { renderComponent } from '@/__tests__/unit/render';
4
- import HelloWorld from '@/components/indexPage/HelloWorld.vue';
4
+ import HelloWorld from '@/pages/home/components/HelloWorld.vue';
5
5
  import i18n from '@/plugins/i18n';
6
6
  import { useConfigStore } from '@/stores';
7
7
 
@@ -0,0 +1 @@
1
+ export { default as SvgIcon } from './src/index.vue';
@@ -6,10 +6,9 @@ import i18NPlugin from '@/plugins/i18n';
6
6
  import router from '@/router';
7
7
  import App from './App.vue';
8
8
  import 'virtual:svg-icons-register';
9
- import '@/styles/tailwind.postcss';
10
- import '@/styles/index.postcss';
9
+ import '@/styles/index.css';
11
10
 
12
- if (process.env.NODE_ENV === 'mockServiceWorker') {
11
+ if (import.meta.env.VITE_APP_MOCK === 'service-worker') {
13
12
  const { worker } = await import('@/mocks/browser');
14
13
 
15
14
  worker.start();
@@ -18,12 +18,7 @@ function changeLocale() {
18
18
  <h1>{{ msg }}</h1>
19
19
 
20
20
  <div class="p-8">
21
- <button
22
- type="button"
23
- @click="count++"
24
- >
25
- count is {{ count }}
26
- </button>
21
+ <button @click="count++">count is {{ count }}</button>
27
22
  </div>
28
23
 
29
24
  <p>
@@ -52,8 +47,8 @@ function changeLocale() {
52
47
  </button>
53
48
  </template>
54
49
 
55
- <style lang="postcss" scoped>
50
+ <style lang="css" scoped>
56
51
  .read-the-docs {
57
- @apply text-[#888];
52
+ color: #888;
58
53
  }
59
54
  </style>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import HelloWorld from '@/components/indexPage/HelloWorld.vue';
2
+ import { SvgIcon } from '@/components/svg-icon';
3
+ import HelloWorld from './components/HelloWorld.vue';
3
4
  </script>
4
5
 
5
6
  <template>
@@ -24,11 +25,21 @@ import HelloWorld from '@/components/indexPage/HelloWorld.vue';
24
25
  />
25
26
  </a>
26
27
  </div>
28
+
27
29
  <hello-world msg="Vite + Vue" />
28
30
  </template>
29
31
 
30
- <style lang="postcss" scoped>
32
+ <style lang="css" scoped>
31
33
  .logo {
32
- @apply w-24 h-24 p-6 mx-auto will-change-[filter] transition-[filter] duration-300 hover:drop-shadow-[0_0_2em_#646cffaa];
34
+ width: 96px;
35
+ height: 96px;
36
+ margin: 0 auto;
37
+ padding: 24px;
38
+ will-change: filter;
39
+ transition: filter 300ms;
40
+
41
+ &:hover {
42
+ filter: drop-shadow(0 0 2em #646cffaa);
43
+ }
33
44
  }
34
45
  </style>
@@ -1,5 +1,5 @@
1
1
  import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
2
- import IndexPage from '@/pages/IndexPage.vue';
2
+ import HomePage from '@/pages/home/index.vue';
3
3
 
4
4
  export const routes: Array<RouteRecordRaw> = [
5
5
  {
@@ -8,7 +8,7 @@ export const routes: Array<RouteRecordRaw> = [
8
8
  },
9
9
  {
10
10
  path: '/',
11
- component: IndexPage,
11
+ component: HomePage,
12
12
  meta: {
13
13
  title: 'Vue3 Coding Standards',
14
14
  },
@@ -9,7 +9,7 @@ const defaultState = {
9
9
  version: versionString,
10
10
  };
11
11
 
12
- export default defineStore('coding-standards-config', {
12
+ export default defineStore('{{projectName}}-config', {
13
13
  state: () => defaultState,
14
14
  getters: {},
15
15
  actions: {},
@@ -0,0 +1,48 @@
1
+ :root {
2
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ -webkit-text-size-adjust: 100%;
15
+ }
16
+
17
+ @media (prefers-color-scheme: light) {
18
+ :root {
19
+ background-color: #ffffff;
20
+ color: #213547;
21
+ }
22
+ }
23
+
24
+ body {
25
+ margin: 0;
26
+ display: flex;
27
+ place-items: center;
28
+ min-width: 320px;
29
+ min-height: 100vh;
30
+ }
31
+
32
+ #app {
33
+ max-width: 80rem;
34
+ margin: auto;
35
+ padding: 32px;
36
+ text-align: center;
37
+ }
38
+
39
+ a {
40
+ font-weight: 500;
41
+ text-decoration-color: inherit;
42
+ color: #646cff;
43
+ transition: color 150ms;
44
+
45
+ &:hover {
46
+ color: #535bf2;
47
+ }
48
+ }
@@ -0,0 +1 @@
1
+ @import './button.css';
@@ -0,0 +1,15 @@
1
+ button {
2
+ border-radius: 8px;
3
+ padding: 8px;
4
+ border-width: 1px;
5
+ border-color: transparent;
6
+ transition-property: border-color;
7
+ transition-duration: 300ms;
8
+ &:hover {
9
+ border-color: #646cff;
10
+ }
11
+ &:focus {
12
+ outline-width: 4px;
13
+ outline-color: -webkit-focus-ring-color;
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ @import './tailwind.css';
2
+ @import './base.css';
3
+ @import './common/all.css';
@@ -0,0 +1 @@
1
+ @import 'tailwindcss';
@@ -9,4 +9,5 @@ declare module '*.vue' {
9
9
  interface ImportMetaEnv extends Readonly<Record<string, string>> {
10
10
  readonly VITE_APP_VERSION: string;
11
11
  readonly VITE_APP_BUILD_EPOCH?: string;
12
+ readonly VITE_APP_MOCK?: string;
12
13
  }
@@ -1,15 +1,19 @@
1
1
  import { dirname, resolve } from 'node:path';
2
2
  import { fileURLToPath, URL } from 'node:url';
3
3
  import vueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
4
+ import tailwindcss from '@tailwindcss/vite';
4
5
  import vue from '@vitejs/plugin-vue';
5
6
  import { visualizer } from 'rollup-plugin-visualizer';
6
- import Components from 'unplugin-vue-components/vite';
7
7
  import { defineConfig } from 'vite';
8
8
  import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
9
9
  import packageJson from './package.json' with { type: 'json' };
10
10
 
11
11
  process.env.VITE_APP_VERSION = packageJson.version;
12
- process.env.NODE_ENV = process.env.MOCK ? 'mockServiceWorker' : process.env.NODE_ENV;
12
+
13
+ if (process.env.MOCK) {
14
+ process.env.VITE_APP_MOCK = 'service-worker';
15
+ }
16
+
13
17
  if (process.env.NODE_ENV === 'production') {
14
18
  process.env.VITE_APP_BUILD_EPOCH = Date.now().toString();
15
19
  }
@@ -18,15 +22,12 @@ export default defineConfig({
18
22
  base: './',
19
23
  plugins: [
20
24
  vue(),
21
- Components({
22
- dirs: ['src/components/common'],
23
- dts: 'src/components.d.ts',
24
- }),
25
+ tailwindcss(),
25
26
  vueI18nPlugin({
26
27
  include: resolve(dirname(fileURLToPath(import.meta.url)), 'src/locales/**'),
27
28
  }),
28
29
  createSvgIconsPlugin({
29
- iconDirs: [resolve(process.cwd(), 'src/assets/images/svgIcons')],
30
+ iconDirs: [resolve(process.cwd(), 'src/assets/images/svg-icons')],
30
31
  }),
31
32
  visualizer({ gzipSize: true }),
32
33
  ],
@@ -1,307 +0,0 @@
1
- /* eslint-disable */
2
- /* tslint:disable */
3
-
4
- /**
5
- * Mock Service Worker.
6
- * @see https://github.com/mswjs/msw
7
- * - Please do NOT modify this file.
8
- * - Please do NOT serve this file on production.
9
- */
10
-
11
- const PACKAGE_VERSION = '2.6.8'
12
- const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
13
- const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
14
- const activeClientIds = new Set()
15
-
16
- self.addEventListener('install', function () {
17
- self.skipWaiting()
18
- })
19
-
20
- self.addEventListener('activate', function (event) {
21
- event.waitUntil(self.clients.claim())
22
- })
23
-
24
- self.addEventListener('message', async function (event) {
25
- const clientId = event.source.id
26
-
27
- if (!clientId || !self.clients) {
28
- return
29
- }
30
-
31
- const client = await self.clients.get(clientId)
32
-
33
- if (!client) {
34
- return
35
- }
36
-
37
- const allClients = await self.clients.matchAll({
38
- type: 'window',
39
- })
40
-
41
- switch (event.data) {
42
- case 'KEEPALIVE_REQUEST': {
43
- sendToClient(client, {
44
- type: 'KEEPALIVE_RESPONSE',
45
- })
46
- break
47
- }
48
-
49
- case 'INTEGRITY_CHECK_REQUEST': {
50
- sendToClient(client, {
51
- type: 'INTEGRITY_CHECK_RESPONSE',
52
- payload: {
53
- packageVersion: PACKAGE_VERSION,
54
- checksum: INTEGRITY_CHECKSUM,
55
- },
56
- })
57
- break
58
- }
59
-
60
- case 'MOCK_ACTIVATE': {
61
- activeClientIds.add(clientId)
62
-
63
- sendToClient(client, {
64
- type: 'MOCKING_ENABLED',
65
- payload: {
66
- client: {
67
- id: client.id,
68
- frameType: client.frameType,
69
- },
70
- },
71
- })
72
- break
73
- }
74
-
75
- case 'MOCK_DEACTIVATE': {
76
- activeClientIds.delete(clientId)
77
- break
78
- }
79
-
80
- case 'CLIENT_CLOSED': {
81
- activeClientIds.delete(clientId)
82
-
83
- const remainingClients = allClients.filter((client) => {
84
- return client.id !== clientId
85
- })
86
-
87
- // Unregister itself when there are no more clients
88
- if (remainingClients.length === 0) {
89
- self.registration.unregister()
90
- }
91
-
92
- break
93
- }
94
- }
95
- })
96
-
97
- self.addEventListener('fetch', function (event) {
98
- const { request } = event
99
-
100
- // Bypass navigation requests.
101
- if (request.mode === 'navigate') {
102
- return
103
- }
104
-
105
- // Opening the DevTools triggers the "only-if-cached" request
106
- // that cannot be handled by the worker. Bypass such requests.
107
- if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
108
- return
109
- }
110
-
111
- // Bypass all requests when there are no active clients.
112
- // Prevents the self-unregistered worked from handling requests
113
- // after it's been deleted (still remains active until the next reload).
114
- if (activeClientIds.size === 0) {
115
- return
116
- }
117
-
118
- // Generate unique request ID.
119
- const requestId = crypto.randomUUID()
120
- event.respondWith(handleRequest(event, requestId))
121
- })
122
-
123
- async function handleRequest(event, requestId) {
124
- const client = await resolveMainClient(event)
125
- const response = await getResponse(event, client, requestId)
126
-
127
- // Send back the response clone for the "response:*" life-cycle events.
128
- // Ensure MSW is active and ready to handle the message, otherwise
129
- // this message will pend indefinitely.
130
- if (client && activeClientIds.has(client.id)) {
131
- ;(async function () {
132
- const responseClone = response.clone()
133
-
134
- sendToClient(
135
- client,
136
- {
137
- type: 'RESPONSE',
138
- payload: {
139
- requestId,
140
- isMockedResponse: IS_MOCKED_RESPONSE in response,
141
- type: responseClone.type,
142
- status: responseClone.status,
143
- statusText: responseClone.statusText,
144
- body: responseClone.body,
145
- headers: Object.fromEntries(responseClone.headers.entries()),
146
- },
147
- },
148
- [responseClone.body],
149
- )
150
- })()
151
- }
152
-
153
- return response
154
- }
155
-
156
- // Resolve the main client for the given event.
157
- // Client that issues a request doesn't necessarily equal the client
158
- // that registered the worker. It's with the latter the worker should
159
- // communicate with during the response resolving phase.
160
- async function resolveMainClient(event) {
161
- const client = await self.clients.get(event.clientId)
162
-
163
- if (activeClientIds.has(event.clientId)) {
164
- return client
165
- }
166
-
167
- if (client?.frameType === 'top-level') {
168
- return client
169
- }
170
-
171
- const allClients = await self.clients.matchAll({
172
- type: 'window',
173
- })
174
-
175
- return allClients
176
- .filter((client) => {
177
- // Get only those clients that are currently visible.
178
- return client.visibilityState === 'visible'
179
- })
180
- .find((client) => {
181
- // Find the client ID that's recorded in the
182
- // set of clients that have registered the worker.
183
- return activeClientIds.has(client.id)
184
- })
185
- }
186
-
187
- async function getResponse(event, client, requestId) {
188
- const { request } = event
189
-
190
- // Clone the request because it might've been already used
191
- // (i.e. its body has been read and sent to the client).
192
- const requestClone = request.clone()
193
-
194
- function passthrough() {
195
- // Cast the request headers to a new Headers instance
196
- // so the headers can be manipulated with.
197
- const headers = new Headers(requestClone.headers)
198
-
199
- // Remove the "accept" header value that marked this request as passthrough.
200
- // This prevents request alteration and also keeps it compliant with the
201
- // user-defined CORS policies.
202
- const acceptHeader = headers.get('accept')
203
- if (acceptHeader) {
204
- const values = acceptHeader.split(',').map((value) => value.trim())
205
- const filteredValues = values.filter(
206
- (value) => value !== 'msw/passthrough',
207
- )
208
-
209
- if (filteredValues.length > 0) {
210
- headers.set('accept', filteredValues.join(', '))
211
- } else {
212
- headers.delete('accept')
213
- }
214
- }
215
-
216
- return fetch(requestClone, { headers })
217
- }
218
-
219
- // Bypass mocking when the client is not active.
220
- if (!client) {
221
- return passthrough()
222
- }
223
-
224
- // Bypass initial page load requests (i.e. static assets).
225
- // The absence of the immediate/parent client in the map of the active clients
226
- // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
227
- // and is not ready to handle requests.
228
- if (!activeClientIds.has(client.id)) {
229
- return passthrough()
230
- }
231
-
232
- // Notify the client that a request has been intercepted.
233
- const requestBuffer = await request.arrayBuffer()
234
- const clientMessage = await sendToClient(
235
- client,
236
- {
237
- type: 'REQUEST',
238
- payload: {
239
- id: requestId,
240
- url: request.url,
241
- mode: request.mode,
242
- method: request.method,
243
- headers: Object.fromEntries(request.headers.entries()),
244
- cache: request.cache,
245
- credentials: request.credentials,
246
- destination: request.destination,
247
- integrity: request.integrity,
248
- redirect: request.redirect,
249
- referrer: request.referrer,
250
- referrerPolicy: request.referrerPolicy,
251
- body: requestBuffer,
252
- keepalive: request.keepalive,
253
- },
254
- },
255
- [requestBuffer],
256
- )
257
-
258
- switch (clientMessage.type) {
259
- case 'MOCK_RESPONSE': {
260
- return respondWithMock(clientMessage.data)
261
- }
262
-
263
- case 'PASSTHROUGH': {
264
- return passthrough()
265
- }
266
- }
267
-
268
- return passthrough()
269
- }
270
-
271
- function sendToClient(client, message, transferrables = []) {
272
- return new Promise((resolve, reject) => {
273
- const channel = new MessageChannel()
274
-
275
- channel.port1.onmessage = (event) => {
276
- if (event.data && event.data.error) {
277
- return reject(event.data.error)
278
- }
279
-
280
- resolve(event.data)
281
- }
282
-
283
- client.postMessage(
284
- message,
285
- [channel.port2].concat(transferrables.filter(Boolean)),
286
- )
287
- })
288
- }
289
-
290
- async function respondWithMock(response) {
291
- // Setting response status code to 0 is a no-op.
292
- // However, when responding with a "Response.error()", the produced Response
293
- // instance will have status code set to 0. Since it's not possible to create
294
- // a Response instance with status code 0, handle that use-case separately.
295
- if (response.status === 0) {
296
- return Response.error()
297
- }
298
-
299
- const mockedResponse = new Response(response.body, response)
300
-
301
- Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
302
- value: true,
303
- enumerable: true,
304
- })
305
-
306
- return mockedResponse
307
- }
@@ -1,14 +0,0 @@
1
- /* eslint-disable */
2
- // @ts-nocheck
3
- // Generated by unplugin-vue-components
4
- // Read more: https://github.com/vuejs/core/pull/3399
5
- export {}
6
-
7
- /* prettier-ignore */
8
- declare module 'vue' {
9
- export interface GlobalComponents {
10
- RouterLink: typeof import('vue-router')['RouterLink']
11
- RouterView: typeof import('vue-router')['RouterView']
12
- SvgIcon: typeof import('./components/common/SvgIcon.vue')['default']
13
- }
14
- }
@@ -1 +0,0 @@
1
- @import './button.postcss';
@@ -1,12 +0,0 @@
1
- button {
2
- @apply
3
- rounded-lg
4
- p-2
5
- border-[1px]
6
- border-transparent
7
- transition-[border-color]
8
- duration-300
9
- hover:border-[#646cff]
10
- focus:outline-4
11
- focus:outline-[-webkit-focus-ring-color];
12
- }
@@ -1 +0,0 @@
1
- @import './common/all.postcss';
@@ -1,39 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- @layer base {
6
- :root {
7
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
8
- line-height: 1.5;
9
- font-weight: 400;
10
-
11
- color-scheme: light dark;
12
- color: rgba(255, 255, 255, 0.87);
13
- background-color: #242424;
14
-
15
- font-synthesis: none;
16
- text-rendering: optimizeLegibility;
17
- -webkit-font-smoothing: antialiased;
18
- -moz-osx-font-smoothing: grayscale;
19
- -webkit-text-size-adjust: 100%;
20
- }
21
-
22
- body {
23
- @apply m-0 flex place-items-center min-w-[320px] min-h-screen;
24
- }
25
-
26
- #app {
27
- @apply max-w-7xl mx-auto p-8 text-center;
28
- }
29
-
30
- a {
31
- @apply font-medium text-[#646cff] decoration-inherit hover:text-[#535bf2];
32
- }
33
-
34
- @media (prefers-color-scheme: light) {
35
- :root {
36
- @apply bg-white text-[#213547];
37
- }
38
- }
39
- }
@@ -1,20 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
-
3
- export default {
4
- content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
5
- theme: {
6
- // fontFamily: {
7
- // sans: ["Monda", "Noto Sans TC", "sans-serif"],
8
- // },
9
- extend: {
10
- colors: {
11
- primary: '',
12
- secondary: '',
13
- },
14
- backgroundImage: {},
15
- animation: {},
16
- keyframes: {},
17
- },
18
- },
19
- plugins: [],
20
- };