├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── commit-msg ├── post-merge └── pre-commit ├── .npmrc ├── .scripts ├── check_pnpm.js └── update_dependencies.sh ├── .sink.d.ts ├── .storybook ├── main.js └── preview.js ├── .stylelintrc.js ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── README.zh.md ├── build-storybook.js ├── commitlint.config.js ├── deploy.config.js ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public └── favicon.ico ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── AboutPage.vue │ ├── HomePage.vue │ ├── TemplateIntroduction.stories.ts │ ├── TemplateIntroduction.vue │ └── WelcomeItem.vue ├── env.d.ts ├── main.ts ├── routes.ts ├── stores │ └── counter.ts ├── stories │ ├── Button.stories.js │ ├── Button.vue │ ├── Header.stories.js │ ├── Header.vue │ ├── Introduction.stories.mdx │ ├── Page.stories.js │ ├── Page.vue │ ├── Themed.stories.ts │ ├── ThemedExample.vue │ ├── assets │ │ ├── code-brackets.svg │ │ ├── colors.svg │ │ ├── comments.svg │ │ ├── direction.svg │ │ ├── flow.svg │ │ ├── plugin.svg │ │ ├── repo.svg │ │ └── stackalt.svg │ ├── button.css │ ├── header.css │ └── page.css └── styles │ └── main.less ├── tsconfig.json ├── vite.config.ts └── windi.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | !.storybook 4 | !.*.js 5 | !.*.ts 6 | !.scripts 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: [ 4 | 'standard-with-typescript', 5 | 'plugin:vue/vue3-recommended' 6 | ], 7 | // We have to explicit set the parser: https://stackoverflow.com/a/66525357 8 | parserOptions: { 9 | parser: '@typescript-eslint/parser' 10 | }, 11 | // https://eslint.vuejs.org/user-guide/#compiler-macros-such-as-defineprops-and-defineemits-generate-no-undef-warnings 12 | env: { 13 | 'vue/setup-compiler-macros': true 14 | }, 15 | overrides: [ 16 | { 17 | files: ['*.ts', '*.tsx'], 18 | parserOptions: { 19 | project: ['./tsconfig.json'] 20 | }, 21 | rules: { 22 | // The core 'no-undef' rules does not work with type definitions 23 | 'no-undef': 'off' 24 | } 25 | }, 26 | // storybook specific rules 27 | { 28 | files: ['**/**.stories.ts', '**/**.stories.tsx'], 29 | rules: { 30 | 'import/no-anonymous-default-export': 0, 31 | 'no-console': 0, 32 | '@typescript-eslint/consistent-type-assertions': 0 33 | 34 | } 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | # Avoid triggering two build on PR 3 | # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662/2 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build-and-prerelease: 14 | runs-on: macos-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-node@v2 18 | with: 19 | node-version: '14' 20 | - uses: pnpm/action-setup@v2.0.1 21 | with: 22 | version: 6.0.2 23 | 24 | - name: Install and Build 🔧 25 | run: | 26 | pnpm i 27 | pnpm run build 28 | pnpm run build-storybook 29 | 30 | - name: Deploy 🚀 31 | uses: JamesIves/github-pages-deploy-action@4.1.5 32 | with: 33 | branch: gh-pages # The branch the action should deploy to. 34 | folder: dist # The folder the action should deploy. 35 | git-config-name: 'github-actions[bot]' 36 | git-config-email: 'github-actions[bot]@users.noreply.github.com' 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpx commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/post-merge: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | sh .scripts/update_dependencies.sh 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpx lint-staged 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Make storybook happy with its deps 2 | shamefully-hoist=true 3 | -------------------------------------------------------------------------------- /.scripts/check_pnpm.js: -------------------------------------------------------------------------------- 1 | if (!/pnpm\.[cm]js$/.test(process.env.npm_execpath || '')) { 2 | console.warn( 3 | '\u001b[33mThis repository requires pnpm for scripts to work properly.\u001b[39m\n' 4 | ) 5 | process.exit(1) 6 | } 7 | -------------------------------------------------------------------------------- /.scripts/update_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # MIT © Sindre Sorhus - sindresorhus.com 3 | # 当 git pull 的时候发现以来有更新的话,pnpm i 一下,参考链接: 4 | # https://gist.github.com/sindresorhus/7996717 5 | # https://gist.github.com/GianlucaGuarini/8001627 6 | 7 | # git hook to run a command after `git pull` if a specified file was changed 8 | # Run `chmod +x post-merge` to make it executable then put it into `.git/hooks/`. 9 | 10 | changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)" 11 | 12 | check_run() { 13 | echo "$changed_files" | grep -E --quiet "$1" && eval "$2" 14 | } 15 | 16 | # Example usage 17 | # In this example it's used to run `pnpm install` if package.json changed 18 | check_run "package.json|pnpm-lock.yaml" "pnpm install" 19 | -------------------------------------------------------------------------------- /.sink.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upupming/vue3-compact-template/9f9efecb82a15628be7a6801cc5c48e309a2ba54/.sink.d.ts -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const WindiCSS = require('vite-plugin-windicss').default 3 | const deployConfig = require('../deploy.config') 4 | 5 | module.exports = { 6 | stories: [ 7 | '../src/**/*.stories.mdx', 8 | '../src/**/*.stories.@(js|jsx|ts|tsx)' 9 | ], 10 | addons: [ 11 | '@storybook/addon-links', 12 | '@storybook/addon-essentials' 13 | ], 14 | framework: '@storybook/vue3', 15 | core: { 16 | builder: 'storybook-builder-vite' 17 | }, 18 | async viteFinal (config, { configType }) { 19 | // customize the Vite config here 20 | config.resolve.alias['@/'] = `${path.resolve(__dirname, '../src')}/` 21 | 22 | config.plugins.push(WindiCSS()) 23 | 24 | // solve cjs name export not able to be converted to esm problem 25 | // https://github.com/vitejs/vite/issues/813#issuecomment-691799104 26 | // https://github.com/eirslett/storybook-builder-vite/issues/132 27 | config.optimizeDeps = config.optimizeDeps || {} 28 | config.optimizeDeps.include = config.optimizeDeps.include || [] 29 | config.optimizeDeps.include.push('synchronous-promise') 30 | 31 | // https://github.com/storybookjs/storybook/issues/10887#issuecomment-901109891 32 | config.resolve.dedupe = ['@storybook/client-api'] 33 | 34 | config.base = `/${deployConfig.path}/${deployConfig.storybookFolder}/` 35 | 36 | // return the customized config 37 | return config 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/a/67001933 2 | import { app } from '@storybook/vue3' 3 | // CSS must be imported before .vue files to ensure that the styles defined in .vue files cam overwrite the default base styles 4 | // windicss layers 5 | import 'virtual:windi-base.css' 6 | import 'virtual:windi-components.css' 7 | // windicss utilities should be the last style import 8 | import 'virtual:windi-utilities.css' 9 | // windicss devtools support (dev only) 10 | import 'virtual:windi-devtools' 11 | import { createPinia } from 'pinia' 12 | 13 | export const parameters = { 14 | actions: { argTypesRegex: '^on[A-Z].*' }, 15 | controls: { 16 | matchers: { 17 | color: /(background|color)$/i, 18 | date: /Date$/ 19 | } 20 | } 21 | } 22 | const pinia = createPinia() 23 | app.use(pinia) 24 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'stylelint-config-standard', 4 | './node_modules/prettier-stylelint/config.js', 5 | // https://www.npmjs.com/package/postcss-html 6 | 'stylelint-config-html', 7 | 'stylelint-config-recommended-vue', 8 | 'stylelint-config-recommended-less' 9 | ], 10 | customSyntax: 'postcss-less', 11 | rules: { 12 | 'less/no-duplicate-variables': null 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "coenraads.bracket-pair-colorizer", 4 | "johnsoncodehk.volar", 5 | "streetsidesoftware.code-spell-checker", 6 | "naumovs.color-highlight", 7 | "dbaeumer.vscode-eslint", 8 | "eamodio.gitlens", 9 | "wix.vscode-import-cost", 10 | "pkief.material-icon-theme", 11 | "esbenp.prettier-vscode", 12 | "yzhang.markdown-all-in-one", 13 | "davidanson.vscode-markdownlint", 14 | "stylelint.vscode-stylelint", 15 | "voorjaar.windicss-intellisense", 16 | // This plugin makes import { xxx } from 'xxx.vue' works in ts: https://github.com/johnsoncodehk/volar/issues/664 17 | "johnsoncodehk.vscode-typescript-vue-plugin" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use eslint to format js/ts files 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": true, 5 | "source.fixAll.stylelint": true 6 | }, 7 | "editor.wordWrap": "on", 8 | // Use prettier to format files 9 | "editor.formatOnSave": true, 10 | // Since we use eslint to format js/ts files, so we have to disable prettier to avoid confilcts 11 | "[javascript]": { 12 | "editor.formatOnSave": false 13 | }, 14 | "[javascriptreact]": { 15 | "editor.formatOnSave": false 16 | }, 17 | "[typescript]": { 18 | "editor.formatOnSave": false 19 | }, 20 | "[typescriptreact]": { 21 | "editor.formatOnSave": false 22 | }, 23 | // Disable css built-in lint 24 | "css.validate": false, 25 | "scss.validate": false, 26 | "less.validate": false, 27 | "stylelint.enable": true, 28 | "stylelint.validate": ["css", "less", "html", "vue"], 29 | "[css]": { 30 | "editor.formatOnSave": false 31 | }, 32 | "[less]": { 33 | "editor.formatOnSave": false 34 | }, 35 | "[scss]": { 36 | "editor.formatOnSave": false 37 | }, 38 | "[vue]": { 39 | "editor.formatOnSave": false 40 | }, 41 | "editor.defaultFormatter": "esbenp.prettier-vscode", 42 | "editor.tabSize": 2, 43 | "files.insertFinalNewline": true, 44 | "cSpell.words": [ 45 | "commitlint", 46 | "pinia", 47 | "pnpm", 48 | "stylelint", 49 | "unplugin", 50 | "vite", 51 | "vueuse", 52 | "windi", 53 | "windicss" 54 | ], 55 | "editor.detectIndentation": false, 56 | "[markdown]": { 57 | "editor.formatOnSave": false, 58 | "editor.tabSize": 4, 59 | "editor.defaultFormatter": "yzhang.markdown-all-in-one" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-present upupming 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-compact-template 2 | 3 | English | [中文](README.zh.md) 4 | 5 | [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com) 6 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square)](https://conventionalcommits.org) 7 | 8 | ⚡️ A simple and compact vue 3 template with current cutting edge front end technologies. 9 | 10 | See the live demo at: 11 | 12 | - http://upupming.site/vue3-compact-template/ 13 | - http://upupming.site/vue3-compact-template/storybook/ 14 | 15 | ## Tech stack 16 | 17 | - Framework: [Vue 3](https://v3.vuejs.org/) 18 | - Package manager: [pnpm](https://github.com/pnpm/pnpm) 19 | - Build tool: [Vite 2](https://vitejs.dev/) 20 | - State management: [Pinia](https://pinia.esm.dev/) 21 | - Router: [Vue Router](https://next.router.vuejs.org/) 22 | - Utility first CSS framework: [WindiCSS](https://windicss.org/) 23 | - CSS preprocessor: [Less](https://lesscss.org/) 24 | - UI component explorer: [Storybook](https://github.com/storybookjs/storybook/) 25 | - Icons: [unplugin-icons](https://github.com/antfu/unplugin-icons) 26 | - ESLint rule: [eslint-config-standard-with-typescript](https://github.com/standard/eslint-config-standard-with-typescript) 27 | - Style linting: stylelint standard 28 | - commit convention: husky + commitlint 29 | 30 | ## Development 31 | 32 | ```bash 33 | # Development 34 | pnpm run dev 35 | # Run storybook 36 | pnpm run storybook 37 | ``` 38 | 39 | ## Build 40 | 41 | ```bash 42 | pnpm run build 43 | pnpm run build-storybook 44 | ``` 45 | 46 | After built, the `dist` folder will contain both the app and storybook build result, you can just serve them using `pnpm run serve`. 47 | 48 | - http://localhost:5000/vue3-compact-template/ 49 | - http://localhost:5000/vue3-compact-template/storybook/ 50 | 51 | ### Recommendations 52 | 53 | - Component library: [NaiveUI](https://www.naiveui.com/zh-CN/os-theme), [arco design](https://arco.design/), [Varlet](https://github.com/haoziqaq/varlet) 54 | - [VueUse](https://github.com/antfu/vueuse) - collection of useful composition APIs 55 | - [`@vueuse/head`](https://github.com/vueuse/head) - manipulate document head reactively 56 | 57 | ## References 58 | 59 | - https://github.com/antfu/vitesse 60 | - [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) 61 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # vue3-compact-template 2 | 3 | [English](README.md) | 中文 4 | 5 | [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com) 6 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square)](https://conventionalcommits.org) 7 | 8 | ⚡️ 一个包含了当前最新的前端技术的简单紧凑的 Vue 3 模板。 9 | 10 | 前往以下地址查看 Demo: 11 | 12 | - http://upupming.site/vue3-compact-template/ 13 | - http://upupming.site/vue3-compact-template/storybook/ 14 | 15 | ## 技术栈 16 | 17 | - 前端框架: [Vue 3](https://v3.vuejs.org/) 18 | - 包管理器: [pnpm](https://github.com/pnpm/pnpm) 19 | - 构建工具: [Vite 2](https://vitejs.dev/) 20 | - 状态管理: [Pinia](https://pinia.esm.dev/) 21 | - 路由: [Vue Router](https://next.router.vuejs.org/) 22 | - 功能类优先 CSS 框架: [WindiCSS](https://windicss.org/) 23 | - CSS 预编译器: [Less](https://lesscss.org/) 24 | - UI 组件浏览工具: [Storybook](https://github.com/storybookjs/storybook/) 25 | - 图标: [unplugin-icons](https://github.com/antfu/unplugin-icons) 26 | - ESLint 规则: [eslint-config-standard-with-typescript](https://github.com/standard/eslint-config-standard-with-typescript) 27 | - 样式规范: stylelint standard 28 | - commit 规范: husky + commitlint 29 | 30 | ## 本地开发 31 | 32 | ```bash 33 | # 开发 34 | pnpm run dev 35 | # 运行 storybook 36 | pnpm run storybook 37 | ``` 38 | 39 | ## 构建 40 | 41 | ```bash 42 | pnpm run build 43 | pnpm run build-storybook 44 | ``` 45 | 46 | 构建之后,`dist` 文件夹中会包含项目和 Storybook 的静态资源文件。你可以使用 `pnpm run serve` 使用本地服务器来 serve 这些静态资源。 47 | 48 | - http://localhost:5000/vue3-compact-template/ 49 | - http://localhost:5000/vue3-compact-template/storybook/ 50 | 51 | ### 推荐搭配 52 | 53 | - 组件库: [NaiveUI](https://www.naiveui.com/zh-CN/os-theme), [arco design](https://arco.design/), [Varlet](https://github.com/haoziqaq/varlet) 54 | - [VueUse](https://github.com/antfu/vueuse) - 一些有用的 composition API 55 | - [`@vueuse/head`](https://github.com/vueuse/head) - 响应式修改 `head` 56 | 57 | ## 参考链接 58 | 59 | - https://github.com/antfu/vitesse 60 | - [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) 61 | -------------------------------------------------------------------------------- /build-storybook.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require('child_process') 2 | const deployConfig = require('./deploy.config') 3 | 4 | spawn( 5 | /^win/.test(process.platform) ? 'pnpx.cmd' : 'pnpx', 6 | [ 7 | 'build-storybook', 8 | '-o', `dist/${deployConfig.storybookFolder}` 9 | ], 10 | { stdio: 'inherit' } 11 | ) 12 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-angular'] 3 | } 4 | -------------------------------------------------------------------------------- /deploy.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | path: 'vue3-compact-template', 3 | storybookFolder: 'storybook' 4 | } 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | vue3-compact-template 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-compact-template", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "preinstall": "node ./.scripts/check_pnpm.js", 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "prepare": "husky install", 9 | "serve": "vite preview", 10 | "lint:eslint": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue .", 11 | "lint:stylelint": "stylelint --fix **/*.{css,less,html,vue}", 12 | "lint": "pnpm run lint:eslint && pnpm run lint:stylelint", 13 | "storybook": "start-storybook -p 6006", 14 | "build-storybook": "node build-storybook.js" 15 | }, 16 | "dependencies": { 17 | "pinia": "^2.0.0", 18 | "vue": "^3.2.16", 19 | "vue-router": "4" 20 | }, 21 | "lint-staged": { 22 | "*.{js,jsx,ts,tsx,mjs,mjsx,cjs,cjsx,vue}": [ 23 | "eslint --fix --color" 24 | ], 25 | "*.{css,less,html,vue}": [ 26 | "stylelint --fix" 27 | ] 28 | }, 29 | "devDependencies": { 30 | "@babel/core": "^7.16.0", 31 | "@commitlint/cli": "^13.2.1", 32 | "@commitlint/config-angular": "^13.2.0", 33 | "@commitlint/prompt-cli": "^13.2.1", 34 | "@iconify-json/carbon": "^1.0.8", 35 | "@iconify-json/fluent": "^1.0.6", 36 | "@iconify-json/gg": "^1.0.1", 37 | "@iconify-json/mdi": "^1.0.7", 38 | "@iconify-json/simple-icons": "^1.0.7", 39 | "@storybook/addon-actions": "^6.4.0-beta.23", 40 | "@storybook/addon-essentials": "^6.4.0-beta.23", 41 | "@storybook/addon-links": "^6.4.0-beta.23", 42 | "@storybook/vue3": "^6.4.0-beta.23", 43 | "@types/node": "^16.11.6", 44 | "@typescript-eslint/eslint-plugin": "^4.33.0", 45 | "@vitejs/plugin-vue": "^1.9.3", 46 | "babel-loader": "^8.2.3", 47 | "eslint": "^7.32.0", 48 | "eslint-config-standard-with-typescript": "^21.0.1", 49 | "eslint-plugin-import": "^2.22.1", 50 | "eslint-plugin-node": "^11.1.0", 51 | "eslint-plugin-promise": "^5.0.0", 52 | "eslint-plugin-vue": "^8.0.3", 53 | "husky": "^7.0.2", 54 | "less": "^4.1.2", 55 | "lint-staged": "^11.2.2", 56 | "prettier-stylelint": "^0.4.2", 57 | "standard-version": "^9.3.1", 58 | "storybook-builder-vite": "^0.1.3", 59 | "stylelint": "^14.0.1", 60 | "stylelint-config-html": "^1.0.0", 61 | "stylelint-config-recommended": "^6.0.0", 62 | "stylelint-config-recommended-less": "^1.0.1", 63 | "stylelint-config-recommended-vue": "^1.0.0", 64 | "stylelint-config-standard": "^23.0.0", 65 | "stylelint-less": "^1.0.1", 66 | "tailwindcss-themeable": "^1.3.0", 67 | "typescript": "^4.4.4", 68 | "unplugin-icons": "^0.12.17", 69 | "vite": "^2.6.14", 70 | "vite-plugin-windicss": "^1.5.4", 71 | "vue-loader": "^16.8.2", 72 | "vue-tsc": "^0.3.0", 73 | "windicss": "^3.2.1" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upupming/vue3-compact-template/9f9efecb82a15628be7a6801cc5c48e309a2ba54/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 52 | 53 | 93 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upupming/vue3-compact-template/9f9efecb82a15628be7a6801cc5c48e309a2ba54/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/AboutPage.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/HomePage.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 143 | -------------------------------------------------------------------------------- /src/components/TemplateIntroduction.stories.ts: -------------------------------------------------------------------------------- 1 | import TemplateIntroduction, { TemplateIntroductionProps } from './TemplateIntroduction.vue' 2 | import { action } from '@storybook/addon-actions' 3 | import { Meta, Story } from '@storybook/vue3' 4 | 5 | export default { 6 | title: 'Components', 7 | component: TemplateIntroduction, 8 | argTypes: { 9 | msg: { 10 | // https://storybook.js.org/docs/react/essentials/controls#annotation 11 | control: { 12 | type: 'text' 13 | } 14 | } 15 | } 16 | } as Meta 17 | 18 | const Template: Story = (args) => ({ 19 | components: { TemplateIntroduction }, 20 | // https://github.com/storybookjs/storybook/discussions/11372#discussioncomment-32947 21 | methods: { update: action('update') }, 22 | setup () { 23 | return { args } 24 | }, 25 | template: '' 26 | }) 27 | 28 | export const TemplateIntroductionDemo = Template.bind({}) 29 | TemplateIntroductionDemo.args = { 30 | msg: '⚡️ A simple and compact vue 3 template with current cutting edge front end technologies.' 31 | } 32 | -------------------------------------------------------------------------------- /src/components/TemplateIntroduction.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 113 | 114 | 143 | -------------------------------------------------------------------------------- /src/components/WelcomeItem.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 73 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | // CSS must be imported before .vue files to ensure that the styles defined in .vue files cam overwrite the default base styles 2 | // windicss layers 3 | import 'virtual:windi-base.css' 4 | import 'virtual:windi-components.css' 5 | // your custom styles here 6 | import './styles/main.less' 7 | // windicss utilities should be the last style import 8 | import 'virtual:windi-utilities.css' 9 | // windicss devtools support (dev only) 10 | import 'virtual:windi-devtools' 11 | 12 | import { createApp } from 'vue' 13 | import { createPinia } from 'pinia' 14 | import { createRouter, createWebHashHistory } from 'vue-router' 15 | import App from './App.vue' 16 | import { routes } from './routes' 17 | 18 | const router = createRouter({ 19 | // 4. Provide the history implementation to use. We are using the hash history for simplicity here. 20 | history: createWebHashHistory(), 21 | routes 22 | }) 23 | 24 | const app = createApp(App) 25 | 26 | const pinia = createPinia() 27 | app.use(pinia) 28 | 29 | app.use(router) 30 | 31 | app.mount('#app') 32 | -------------------------------------------------------------------------------- /src/routes.ts: -------------------------------------------------------------------------------- 1 | import HomePage from '@/components/HomePage.vue' 2 | import AboutPage from '@/components/AboutPage.vue' 3 | export const routes = [ 4 | { path: '/', component: HomePage }, 5 | { path: '/about', component: AboutPage } 6 | ] 7 | -------------------------------------------------------------------------------- /src/stores/counter.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export const useCounterStore = defineStore('counter', { 4 | state: () => { 5 | return { count: 0 } 6 | }, 7 | // could also be defined as 8 | // state: () => ({ count: 0 }) 9 | actions: { 10 | increment () { 11 | this.count++ 12 | } 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /src/stories/Button.stories.js: -------------------------------------------------------------------------------- 1 | import MyButton from './Button.vue' 2 | 3 | // More on default export: https://storybook.js.org/docs/vue/writing-stories/introduction#default-export 4 | export default { 5 | title: 'Example/Button', 6 | component: MyButton, 7 | // More on argTypes: https://storybook.js.org/docs/vue/api/argtypes 8 | argTypes: { 9 | backgroundColor: { control: 'color' }, 10 | onClick: {}, 11 | size: { 12 | control: { type: 'select' }, 13 | options: ['small', 'medium', 'large'] 14 | } 15 | } 16 | } 17 | 18 | // More on component templates: https://storybook.js.org/docs/vue/writing-stories/introduction#using-args 19 | const Template = (args) => ({ 20 | // Components used in your story `template` are defined in the `components` object 21 | components: { MyButton }, 22 | // The story's `args` need to be mapped into the template through the `setup()` method 23 | setup () { 24 | return { args } 25 | }, 26 | // And then the `args` are bound to your component with `v-bind="args"` 27 | template: '' 28 | }) 29 | 30 | export const Primary = Template.bind({}) 31 | // More on args: https://storybook.js.org/docs/vue/writing-stories/args 32 | Primary.args = { 33 | primary: true, 34 | label: 'Button' 35 | } 36 | 37 | export const Secondary = Template.bind({}) 38 | Secondary.args = { 39 | label: 'Button' 40 | } 41 | 42 | export const Large = Template.bind({}) 43 | Large.args = { 44 | size: 'large', 45 | label: 'Button' 46 | } 47 | 48 | export const Small = Template.bind({}) 49 | Small.args = { 50 | size: 'small', 51 | label: 'Button' 52 | } 53 | -------------------------------------------------------------------------------- /src/stories/Button.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 60 | -------------------------------------------------------------------------------- /src/stories/Header.stories.js: -------------------------------------------------------------------------------- 1 | import MyHeader from './Header.vue' 2 | 3 | export default { 4 | title: 'Example/Header', 5 | component: MyHeader 6 | } 7 | 8 | const Template = (args) => ({ 9 | // Components used in your story `template` are defined in the `components` object 10 | components: { MyHeader }, 11 | // The story's `args` need to be mapped into the template through the `setup()` method 12 | setup () { 13 | // Story args can be spread into the returned object 14 | return { ...args } 15 | }, 16 | // Then, the spread values can be accessed directly in the template 17 | template: '' 18 | }) 19 | 20 | export const LoggedIn = Template.bind({}) 21 | LoggedIn.args = { 22 | user: {} 23 | } 24 | 25 | export const LoggedOut = Template.bind({}) 26 | LoggedOut.args = { 27 | user: null 28 | } 29 | -------------------------------------------------------------------------------- /src/stories/Header.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 74 | -------------------------------------------------------------------------------- /src/stories/Introduction.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | import Code from './assets/code-brackets.svg'; 3 | import Colors from './assets/colors.svg'; 4 | import Comments from './assets/comments.svg'; 5 | import Direction from './assets/direction.svg'; 6 | import Flow from './assets/flow.svg'; 7 | import Plugin from './assets/plugin.svg'; 8 | import Repo from './assets/repo.svg'; 9 | import StackAlt from './assets/stackalt.svg'; 10 | 11 | 12 | 13 | 116 | 117 | # Welcome to Storybook 118 | 119 | Storybook helps you build UI components in isolation from your app's business logic, data, and context. 120 | That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA. 121 | 122 | Browse example stories now by navigating to them in the sidebar. 123 | View their code in the `src/stories` directory to learn how they work. 124 | We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages. 125 | 126 |
Configure
127 | 128 | 174 | 175 |
Learn
176 | 177 | 207 | 208 |
209 | TipEdit the Markdown in{' '} 210 | src/stories/Introduction.stories.mdx 211 |
212 | -------------------------------------------------------------------------------- /src/stories/Page.stories.js: -------------------------------------------------------------------------------- 1 | import MyPage from './Page.vue' 2 | import * as HeaderStories from './Header.stories' 3 | 4 | export default { 5 | title: 'Example/Page', 6 | component: MyPage 7 | } 8 | 9 | const Template = (args) => ({ 10 | // Components used in your story `template` are defined in the `components` object 11 | components: { MyPage }, 12 | // The story's `args` need to be mapped into the template through the `setup()` method 13 | setup () { 14 | // Story args can be mapped to keys in the returned object 15 | return { user: args.user } 16 | }, 17 | // Then, those values can be accessed directly in the template 18 | template: '' 19 | }) 20 | 21 | export const LoggedIn = Template.bind({}) 22 | LoggedIn.args = { 23 | // More on composing args: https://storybook.js.org/docs/vue/writing-stories/args#args-composition 24 | ...HeaderStories.LoggedIn.args 25 | } 26 | 27 | export const LoggedOut = Template.bind({}) 28 | LoggedOut.args = { 29 | ...HeaderStories.LoggedOut.args 30 | } 31 | -------------------------------------------------------------------------------- /src/stories/Page.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 97 | -------------------------------------------------------------------------------- /src/stories/Themed.stories.ts: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from '@storybook/vue3' 2 | import ThemedExample from './ThemedExample.vue' 3 | 4 | /** 5 | * https://www.youtube.com/watch?v=MAtaT8BZEAo&ab_channel=TailwindLabs 6 | * https://play.tailwindcss.com/YelhilBeHb 7 | */ 8 | export default { 9 | title: 'Components' 10 | } as Meta 11 | 12 | export const Themed: Story = (args) => ({ 13 | components: { 14 | ThemedExample 15 | }, 16 | template: '' 17 | }) 18 | -------------------------------------------------------------------------------- /src/stories/ThemedExample.vue: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /src/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /src/stories/assets/colors.svg: -------------------------------------------------------------------------------- 1 | illustration/colors -------------------------------------------------------------------------------- /src/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /src/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /src/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /src/stories/assets/plugin.svg: -------------------------------------------------------------------------------- 1 | illustration/plugin -------------------------------------------------------------------------------- /src/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /src/stories/assets/stackalt.svg: -------------------------------------------------------------------------------- 1 | illustration/stackalt -------------------------------------------------------------------------------- /src/stories/button.css: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 3em; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | 11 | /* stylelint-disable */ 12 | .storybook-button--primary { 13 | color: white; 14 | background-color: #1ea7fd; 15 | } 16 | 17 | .storybook-button--secondary { 18 | color: #333; 19 | background-color: transparent; 20 | box-shadow: rgb(0 0 0 / 15%) 0 0 0 1px inset; 21 | } 22 | 23 | .storybook-button--small { 24 | font-size: 12px; 25 | padding: 10px 16px; 26 | } 27 | 28 | .storybook-button--medium { 29 | font-size: 14px; 30 | padding: 11px 20px; 31 | } 32 | 33 | .storybook-button--large { 34 | font-size: 16px; 35 | padding: 12px 24px; 36 | } 37 | -------------------------------------------------------------------------------- /src/stories/header.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgb(0 0 0 / 10%); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | h1 { 16 | font-weight: 900; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | button + button { 25 | margin-left: 10px; 26 | } 27 | -------------------------------------------------------------------------------- /src/stories/page.css: -------------------------------------------------------------------------------- 1 | section { 2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | section h2 { 12 | font-weight: 900; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | section p { 21 | margin: 1em 0; 22 | } 23 | 24 | section a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | section ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | section li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | section .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | section .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | section .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | section .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /src/styles/main.less: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #app { 4 | height: 100%; 5 | margin: 0; 6 | padding: 0; 7 | 8 | @apply bg-gray-100; 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "lib": ["esnext", "dom"], 13 | "baseUrl": ".", 14 | "allowJs": true, 15 | "paths": { 16 | "@/*": ["src/*"], 17 | // disable react types as a work around for: https://github.com/storybookjs/storybook/pull/16629 18 | // https://github.com/Microsoft/TypeScript/issues/17042#issuecomment-327882577 19 | "react": [".sink.d.ts"] 20 | }, 21 | "outDir": "out", 22 | "types": ["vite/client", "unplugin-icons/types/vue"], 23 | "skipLibCheck": true 24 | }, 25 | "include": ["**/*.ts", "**/*.d.ts", "**/*.tsx", "**/*.vue"], 26 | "exclude": ["dist", "node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import path from 'path' 3 | import vue from '@vitejs/plugin-vue' 4 | import WindiCSS from 'vite-plugin-windicss' 5 | import deployConfig from './deploy.config.js' 6 | import Icons from 'unplugin-icons/vite' 7 | 8 | // https://vitejs.dev/config/ 9 | export default defineConfig({ 10 | resolve: { 11 | alias: { 12 | '@/': `${path.resolve(__dirname, 'src')}/` 13 | } 14 | }, 15 | plugins: [ 16 | vue(), 17 | WindiCSS(), 18 | Icons({ 19 | autoInstall: true 20 | }) 21 | ], 22 | base: `/${deployConfig.path}/` 23 | }) 24 | -------------------------------------------------------------------------------- /windi.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'windicss/helpers' 2 | import { themeable } from 'tailwindcss-themeable' 3 | 4 | export default defineConfig({ 5 | theme: { 6 | extend: { 7 | } 8 | }, 9 | variants: { 10 | extend: [] 11 | }, 12 | plugins: [themeable()] 13 | } 14 | ) 15 | --------------------------------------------------------------------------------