├── tea.yaml:Zone.Identifier ├── src ├── assets │ ├── index.postcss │ ├── logo.png │ ├── screenshot.png │ ├── networking-svgrepo-com.svg │ ├── like-svgrepo-com.svg │ ├── club.svg │ └── club_with_icon.svg ├── layouts │ ├── default.vue │ ├── about.vue │ └── README.md ├── router.ts ├── i18n.ts ├── main.ts ├── components │ ├── README.md │ ├── ButtonRepo.vue │ ├── HelloWorld.vue │ ├── CopyRight.vue │ ├── MyHeader.vue │ ├── GithubRibbon.vue │ └── MyFooter.vue ├── App.vue ├── pages │ ├── README.md │ ├── tool.vue │ ├── [...all].vue │ ├── about.vue │ ├── index.vue │ └── hi │ │ └── [name].vue ├── stores │ └── counter.ts ├── env.d.ts └── components.d.ts ├── public └── favicon.ico ├── tea.yaml ├── vercel.json ├── CODE_OF_CONDUCT.md ├── tests ├── tsconfig.json ├── unit │ └── example.test.ts ├── e2e │ ├── fixtures │ │ └── example.json │ ├── plugins │ │ └── index.ts │ ├── support │ │ ├── commands.ts │ │ └── index.ts │ └── integration │ │ └── basic_spec.ts └── component │ └── HelloWorld.spec.ts ├── postcss.config.js ├── locales ├── zh-CN.yml ├── en.yml └── README.md ├── CHANGES.md ├── .github └── workflows │ ├── renovate.json │ └── main.yml ├── .gitignore ├── cypress.json ├── index.html ├── tsconfig.json ├── .eslintrc.js ├── LICENSE.md ├── tailwind.config.js ├── vite.config.ts ├── package.json └── README.md /tea.yaml:Zone.Identifier: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/index.postcss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feitian124/vue3-tailwind3-website-starter/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feitian124/vue3-tailwind3-website-starter/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feitian124/vue3-tailwind3-website-starter/HEAD/src/assets/screenshot.png -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0xE8526665010868a6A7269933f501bF1413249431' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/index.html" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This repo adheres to [Contributor Covenant Code of Condict](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es5", "dom"], 5 | "types": ["cypress"] 6 | }, 7 | "include": ["**/*.ts"] 8 | } -------------------------------------------------------------------------------- /tests/unit/example.test.ts: -------------------------------------------------------------------------------- 1 | import { test, assert, describe } from 'vitest' 2 | 3 | describe('example test', () => { 4 | test('assert', () => { 5 | assert.equal(1, 1) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /tests/e2e/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /src/layouts/about.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'tailwindcss/nesting': 'postcss-nesting', 5 | 'tailwindcss': {}, 6 | 'autoprefixer': {}, 7 | ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}), 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /locales/zh-CN.yml: -------------------------------------------------------------------------------- 1 | button: 2 | about: 关于 3 | back: 返回 4 | go: 确定 5 | home: 首页 6 | toggle_dark: 切换深色模式 7 | toggle_langs: 切换语言 8 | intro: 9 | desc: 固执己见的 Vite 项目模板 10 | dynamic-route: 动态路由演示 11 | hi: 你好,{name} 12 | aka: 也叫 13 | whats-your-name: 输入你的名字 14 | not-found: 未找到页面 15 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.1 (2022-03-12) 4 | 5 | - Feat: added Vitest for unit tests. 6 | - Tooling: simplified and improved the GitHub Actions pipeline. 7 | - Docs: added Code of Conduct document. 8 | - Chore: bumped deps. 9 | 10 | ## 1.0.0 (2022-03-05) 11 | 12 | - Initial version 13 | -------------------------------------------------------------------------------- /locales/en.yml: -------------------------------------------------------------------------------- 1 | button: 2 | about: About 3 | back: Back 4 | go: GO 5 | home: Home 6 | toggle_dark: Toggle dark mode 7 | toggle_langs: Change languages 8 | intro: 9 | desc: Opinionated Vite Starter Template 10 | dynamic-route: Demo of dynamic route 11 | hi: Hi, {name}! 12 | aka: Also known as 13 | whats-your-name: What's your name? 14 | not-found: Not found 15 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | 3 | import { setupLayouts } from 'virtual:generated-layouts' 4 | import generatedRoutes from 'virtual:generated-pages' 5 | 6 | const routes = setupLayouts(generatedRoutes) 7 | 8 | const router = createRouter({ 9 | history: createWebHistory(), 10 | routes, 11 | }) 12 | 13 | export default router 14 | -------------------------------------------------------------------------------- /src/i18n.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n' 2 | /* 3 | * All i18n resources specified in the plugin `include` option can be loaded at once using the import syntax 4 | */ 5 | import messages from '@intlify/vite-plugin-vue-i18n/messages' 6 | 7 | const i18n = createI18n({ 8 | legacy: false, 9 | locale: 'zh-CN', 10 | fallbackLocale: 'en', 11 | messages, 12 | }) 13 | 14 | export default i18n 15 | -------------------------------------------------------------------------------- /tests/e2e/plugins/index.ts: -------------------------------------------------------------------------------- 1 | import { startDevServer } from '@cypress/vite-dev-server' 2 | 3 | export default function ( 4 | on: Cypress.PluginEvents, 5 | config: Cypress.PluginConfigOptions 6 | ): void | Cypress.ConfigOptions | Promise { 7 | on('dev-server:start', async (options: Cypress.DevServerConfig) => 8 | startDevServer({ options }) 9 | ) 10 | return config 11 | } 12 | -------------------------------------------------------------------------------- /locales/README.md: -------------------------------------------------------------------------------- 1 | ## i18n 2 | 3 | This directory is to serve your locale translation files. YAML under this folder would be loaded automatically and register with their filenames as locale code. 4 | 5 | Check out [`vue-i18n`](https://github.com/intlify/vue-i18n-next) for more details. 6 | 7 | If you are using VS Code, [`i18n Ally`](https://github.com/lokalise/i18n-ally) is recommended to make the i18n experience better. 8 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createHead } from '@vueuse/head' 3 | import { createPinia } from 'pinia' 4 | 5 | import App from './App.vue' 6 | import router from './router' 7 | import i18n from './i18n' 8 | 9 | import './assets/index.postcss' 10 | 11 | const head = createHead() 12 | const app = createApp(App) 13 | 14 | app.use(router) 15 | app.use(i18n) 16 | app.use(head) 17 | app.use(createPinia()) 18 | 19 | app.mount('#app') 20 | -------------------------------------------------------------------------------- /src/components/README.md: -------------------------------------------------------------------------------- 1 | ## Components 2 | 3 | Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components). 4 | 5 | 6 | ### Icons 7 | 8 | You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/). 9 | 10 | It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details. 11 | -------------------------------------------------------------------------------- /.github/workflows/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | "group:all", 5 | "schedule:weekly" 6 | ], 7 | "packageRules": [ 8 | { 9 | "updateTypes": [ 10 | "patch" 11 | ], 12 | "enabled": false 13 | }, 14 | { 15 | "matchPackagePatterns": [ 16 | "*" 17 | ], 18 | "rangeStrategy": "replace" 19 | } 20 | ], 21 | "timezone": "Asia/Shanghai", 22 | "dependencyDashboard": false 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | dist-ssr 5 | 6 | /tests/e2e/videos/ 7 | /tests/e2e/screenshots/ 8 | /instrumented 9 | /coverage 10 | .nyc_output 11 | 12 | # local env files 13 | .env 14 | .env.local 15 | .env.*.local 16 | 17 | # Log files 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | pnpm-debug.log* 22 | 23 | # Editor directories and files 24 | .idea 25 | .vscode 26 | *.suo 27 | *.ntvs* 28 | *.njsproj 29 | *.sln 30 | *.sw? 31 | 32 | *.local 33 | LOCAL_NOTES.md 34 | -------------------------------------------------------------------------------- /src/components/ButtonRepo.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /src/layouts/README.md: -------------------------------------------------------------------------------- 1 | ## Layouts 2 | 3 | Vue components in this dir are used as layouts. 4 | 5 | By default, `default.vue` will be used unless an alternative is specified in the route meta. 6 | 7 | With [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) and [`vite-plugin-vue-layouts`](https://github.com/JohnCampionJr/vite-plugin-vue-layouts), you can specify the layout in the page's SFCs like this: 8 | 9 | ```html 10 | 11 | meta: 12 | layout: home 13 | 14 | ``` 15 | -------------------------------------------------------------------------------- /src/pages/README.md: -------------------------------------------------------------------------------- 1 | ## File-based Routing 2 | 3 | Routes will be auto-generated for Vue files in this dir with the same file structure. 4 | Check out [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) for more details. 5 | 6 | ### Path Aliasing 7 | 8 | `~/` is aliased to `./src/` folder. 9 | 10 | For example, instead of having 11 | 12 | ```ts 13 | import { isDark } from '../../../../composables' 14 | ``` 15 | 16 | now, you can use 17 | 18 | ```ts 19 | import { isDark } from '~/composables' 20 | ``` 21 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "fixturesFolder": "tests/e2e/fixtures", 4 | "integrationFolder": "tests/e2e/integration", 5 | "pluginsFile": "tests/e2e/plugins/index.ts", 6 | "screenshotsFolder": "tests/e2e/screenshots", 7 | "supportFile": "tests/e2e/support/index.ts", 8 | "videosFolder": "tests/e2e/videos", 9 | "component": { 10 | "componentFolder": "tests/component", 11 | "testFiles": "**/*spec.ts" 12 | }, 13 | "retries": { 14 | "runMode": 2, 15 | "openMode": 0 16 | } 17 | } -------------------------------------------------------------------------------- /src/stores/counter.ts: -------------------------------------------------------------------------------- 1 | import { acceptHMRUpdate, defineStore } from "pinia"; 2 | import { ref } from "vue"; 3 | import { useIntervalFn } from "@vueuse/core"; 4 | 5 | export const useCounterStore = defineStore("counter", () => { 6 | const count = ref(0); 7 | 8 | function increment() { 9 | count.value++; 10 | } 11 | 12 | useIntervalFn(() => { 13 | increment(); 14 | }, 1000); 15 | 16 | return { 17 | count, 18 | increment, 19 | }; 20 | }); 21 | 22 | if (import.meta.hot) 23 | import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot)); 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue 3 + TypeScript + Tailwind Starter Template 8 | 9 | 10 |
11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent 7 | export default component 8 | } 9 | 10 | interface ImportMetaEnv extends Readonly> { 11 | // Only string type here to avoid hard to debug cast problems in your components! 12 | readonly VITE_APP_VERSION: string 13 | readonly VITE_APP_BUILD_EPOCH?: string 14 | } 15 | interface ImportMeta { 16 | readonly env: ImportMetaEnv 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": false, 9 | "lib": ["esnext", "dom"], 10 | "types": ["vite/client", "cypress", "vite-plugin-pages/client", "vite-plugin-vue-layouts/client","@intlify/vite-plugin-vue-i18n/client"], 11 | "resolveJsonModule": true, 12 | "plugins": [{ "name": "@vuedx/typescript-plugin-vue" }], 13 | "paths": { 14 | "@/*": ["./src/*"] 15 | } 16 | }, 17 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"], 18 | "exclude": ["node_modules", "dist", "public", "tests"] 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | '@vue/standard', 9 | '@vue/typescript/recommended', 10 | 'prettier', 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 2020, 14 | }, 15 | rules: { 16 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 17 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 18 | 'comma-dangle': ['error', 'only-multiline'], 19 | }, 20 | globals: { 21 | defineProps: 'readonly', 22 | defineEmits: 'readonly', 23 | defineExpose: 'readonly', 24 | withDefaults: 'readonly', 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /tests/component/HelloWorld.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@cypress/vue' 2 | import { createTestingPinia } from '@pinia/testing' 3 | 4 | import HelloWorld from '@/components/HelloWorld.vue' 5 | 6 | it('Passes msg prop correctly', () => { 7 | mount(HelloWorld, { 8 | propsData: { 9 | msg: 'Hello World', 10 | }, 11 | global: { 12 | plugins: [ 13 | createTestingPinia({ 14 | createSpy: (args) => { 15 | console.log('spy', args) 16 | return () => { 17 | console.log('spyreturns') 18 | } 19 | }, 20 | }), 21 | ], 22 | }, 23 | }) 24 | 25 | cy.get('h2').should('contain.text', 'Hello World') 26 | }) 27 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | -------------------------------------------------------------------------------- /src/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/vue-next/pull/3399 4 | 5 | declare module 'vue' { 6 | export interface GlobalComponents { 7 | ButtonRepo: typeof import('./components/ButtonRepo.vue')['default'] 8 | CopyRight: typeof import('./components/CopyRight.vue')['default'] 9 | GithubRibbon: typeof import('./components/GithubRibbon.vue')['default'] 10 | HelloWorld: typeof import('./components/HelloWorld.vue')['default'] 11 | MyFooter: typeof import('./components/MyFooter.vue')['default'] 12 | MyHeader: typeof import('./components/MyHeader.vue')['default'] 13 | README: typeof import('./components/README.md')['default'] 14 | } 15 | } 16 | 17 | export { } 18 | -------------------------------------------------------------------------------- /src/pages/tool.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/components/CopyRight.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /tests/e2e/integration/basic_spec.ts: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | describe('Homepage', () => { 4 | it('Shows correct text', () => { 5 | cy.visit('/') 6 | 7 | cy.contains('h2', 'Hello World Component') 8 | }) 9 | 10 | it('Should not have vertical scroll bars on mobile or desktop', () => { 11 | cy.viewport('iphone-5') 12 | cy.window().then(() => { 13 | const htmlScrollWidth = Cypress.$('html')[0].scrollWidth 14 | const htmlWidth = Cypress.$('html')[0].clientWidth 15 | const scrollBarWidth = htmlScrollWidth - htmlWidth 16 | expect(scrollBarWidth).to.be.eq(0) 17 | }) 18 | 19 | cy.viewport('macbook-11') 20 | cy.window().then(() => { 21 | const htmlScrollWidth = Cypress.$('html')[0].scrollWidth 22 | const htmlWidth = Cypress.$('html')[0].clientWidth 23 | const scrollBarWidth = htmlScrollWidth - htmlWidth 24 | expect(scrollBarWidth).to.be.eq(0) 25 | }) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/components/MyHeader.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/**' 9 | - 'tests/**' 10 | - 'yarn.lock' 11 | - '.github/workflows/main.yml' 12 | pull_request: 13 | paths: 14 | - 'src/**' 15 | - 'tests/**' 16 | - 'yarn.lock' 17 | - '.github/workflows/main.yml' 18 | 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | tests: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | - uses: bahmutov/npm-install@v1 30 | 31 | ## Test 32 | - name: Run unit tests 33 | run: yarn test:ci 34 | 35 | - name: Run Cypress tests 36 | run: | 37 | yarn start 38 | yarn test:ci-e2e 39 | sleep 2 40 | yarn test:ci-components 41 | 42 | - name: Upload artifacts on fail 43 | uses: actions/upload-artifact@v3 44 | if: failure() 45 | with: 46 | name: videos 47 | path: tests/e2e/videos/ 48 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ville Säävuori 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 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const plugin = require('tailwindcss/plugin') 2 | // const defaultTheme = require('tailwindcss/defaultTheme') 3 | 4 | module.exports = { 5 | content: ['./index.html', './src/**/*.{vue,ts}'], 6 | // theme: { 7 | // extend: { 8 | // // here's how to extend fonts if needed 9 | // fontFamily: { 10 | // sans: [...defaultTheme.fontFamily.sans], 11 | // }, 12 | // }, 13 | // }, 14 | plugins: [ 15 | require('@tailwindcss/aspect-ratio'), 16 | require('@tailwindcss/line-clamp'), 17 | require('@tailwindcss/typography'), 18 | require('@tailwindcss/forms'), 19 | plugin(function ({ addVariant, e, postcss }) { 20 | addVariant('firefox', ({ container, separator }) => { 21 | const isFirefoxRule = postcss.atRule({ 22 | name: '-moz-document', 23 | params: 'url-prefix()', 24 | }) 25 | isFirefoxRule.append(container.nodes) 26 | container.append(isFirefoxRule) 27 | isFirefoxRule.walkRules((rule) => { 28 | rule.selector = `.${e(`firefox${separator}${rule.selector.slice(1)}`)}` 29 | }) 30 | }) 31 | }), 32 | require("daisyui"), 33 | ], 34 | } 35 | -------------------------------------------------------------------------------- /src/components/GithubRibbon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/[...all].vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /tests/e2e/support/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example support/index.js is processed and 4 | // loaded automatically before your test files. 5 | // 6 | // This is a great place to put global configuration and 7 | // behavior that modifies Cypress. 8 | // 9 | // You can change the location of this file or turn off 10 | // automatically serving support files with the 11 | // 'supportFile' configuration option. 12 | // 13 | // You can read more here: 14 | // https://on.cypress.io/configuration 15 | // *********************************************************** 16 | 17 | // Import commands.js using ES2015 syntax: 18 | import './commands' 19 | 20 | Cypress.on('window:before:load', (win) => { 21 | win.handleFromCypress = function (request) { 22 | return fetch(request.url, { 23 | method: request.method, 24 | headers: request.requestHeaders, 25 | body: request.requestBody, 26 | }).then((res) => { 27 | const content = 28 | res.headers.map['content-type'] === 'application/json' ? res.json() : res.text() 29 | return new Promise((resolve) => { 30 | content.then((body) => resolve([res.status, res.headers, body])) 31 | }) 32 | }) 33 | } 34 | }) 35 | -------------------------------------------------------------------------------- /src/pages/about.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 36 | meta: 37 | layout: about 38 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 39 | -------------------------------------------------------------------------------- /src/pages/hi/[name].vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 41 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import * as path from "path"; 4 | import { defineConfig } from "vite"; 5 | import vue from "@vitejs/plugin-vue"; 6 | import vueI18n from "@intlify/vite-plugin-vue-i18n"; 7 | import pkg from "./package.json"; 8 | import pages from "vite-plugin-pages"; 9 | import layouts from "vite-plugin-vue-layouts"; 10 | import components from "unplugin-vue-components/vite"; 11 | 12 | process.env.VITE_APP_VERSION = pkg.version; 13 | if (process.env.NODE_ENV === "production") { 14 | process.env.VITE_APP_BUILD_EPOCH = new Date().getTime().toString(); 15 | } 16 | 17 | export default defineConfig({ 18 | plugins: [ 19 | vue({ 20 | script: { 21 | refSugar: true, 22 | }, 23 | }), 24 | pages(), 25 | layouts(), 26 | 27 | // https://github.com/antfu/unplugin-vue-components 28 | components({ 29 | // allow auto load markdown components under `./src/components/` 30 | extensions: ["vue", "md"], 31 | // allow auto import and register components used in markdown 32 | include: [/\.vue$/, /\.vue\?vue/, /\.md$/], 33 | dts: "src/components.d.ts", 34 | }), 35 | 36 | // https://github.com/intlify/bundle-tools/tree/main/packages/vite-plugin-vue-i18n 37 | vueI18n({ 38 | runtimeOnly: true, 39 | compositionOnly: true, 40 | include: [path.resolve(__dirname, "locales/**")], 41 | }), 42 | ], 43 | resolve: { 44 | alias: { 45 | "@": path.resolve(__dirname, "./src"), 46 | }, 47 | }, 48 | test: { 49 | include: ['tests/unit/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /src/components/MyFooter.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-tailwind3-website-starter", 3 | "description": "a nice website start template use awesome things related to vue3 and tailwind3", 4 | "version": "1.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/feitian124/vue3-tailwind3-website-starter.git" 8 | }, 9 | "keywords": [], 10 | "author": "feitian124 ", 11 | "license": "MIT", 12 | "bugs": { 13 | "url": "https://github.com/feitian124/vue3-tailwind3-website-starter/issues" 14 | }, 15 | "homepage": "https://github.com/feitian124/vue3-tailwind3-website-starter", 16 | "scripts": { 17 | "dev": "vite", 18 | "build": "vite build", 19 | "preview": "vite build && vite preview", 20 | "start": "yarn dev & wait-on tcp:3000 -v", 21 | "test": "vitest", 22 | "test-e2e": "cypress open", 23 | "test-components": "cypress open-ct", 24 | "test:ci": "vitest", 25 | "test:ci-e2e": "cypress run --headless", 26 | "test:ci-components": "cypress run-ct" 27 | }, 28 | "dependencies": { 29 | "@vueuse/core": "~7.7.0", 30 | "@vueuse/head": "~0.7", 31 | "daisyui": "~2.0.9", 32 | "pinia": "~2.0.11", 33 | "vue": "~3.2", 34 | "vue-i18n": "9", 35 | "vue-router": "~4.0" 36 | }, 37 | "devDependencies": { 38 | "@cypress/vite-dev-server": "~2.2", 39 | "@cypress/vue": "~3.0", 40 | "@intlify/vite-plugin-vue-i18n": "~3.3.1", 41 | "@pinia/testing": "~0.0.9", 42 | "@tailwindcss/aspect-ratio": "~0.4", 43 | "@tailwindcss/forms": "~0.4", 44 | "@tailwindcss/line-clamp": "~0.3", 45 | "@tailwindcss/typography": "~0.5", 46 | "@typescript-eslint/eslint-plugin": "~5", 47 | "@typescript-eslint/parser": "~5", 48 | "@vitejs/plugin-vue": "~2.2.4", 49 | "@vue/eslint-config-standard": "~6.1", 50 | "@vue/eslint-config-typescript": "~10", 51 | "autoprefixer": "~10", 52 | "cssnano": "~5.1.3", 53 | "cypress": "~9.5.0", 54 | "eslint": "~8.8", 55 | "eslint-config-prettier": "~8.3", 56 | "eslint-plugin-import": "~2", 57 | "eslint-plugin-node": "~11.1", 58 | "eslint-plugin-promise": "~6.0", 59 | "eslint-plugin-vue": "~8", 60 | "postcss": "~8.4", 61 | "postcss-import": "~14.0.2", 62 | "postcss-nesting": "~10", 63 | "tailwindcss": "~3", 64 | "typescript": "~4.5", 65 | "unplugin-vue-components": "~0.17.18", 66 | "vite": "~2.7", 67 | "vite-plugin-pages": "~0.20.2", 68 | "vite-plugin-vue-layouts": "~0.6.0", 69 | "vitest": "~0.6.0", 70 | "wait-on": "~6.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-tailwind3-website-starter 2 | 3 | A nice and simple website start template using awesome things related to vue3 and tailwind3. 4 | 5 | ![screenshot](src/assets/screenshot.png) 6 | 7 | This template is **practical** and **batteries included** but not overly complex or bloated. Also explicit configuration over magic; it should be easy to understand how everything works, strip out anything you don't need, and fast to modify things to your needs. 8 | 9 | Includes plenty of **examples and documentation** of how to do things but **minimal cruft** to delete to get you going. 10 | 11 | Please check out the [demo](https://vue3-tailwind3-website-starter.vercel.app/) for example. A [detailed changelog](./CHANGES.md) is available. 12 | ## Features 13 | 14 | - Vue 3.2, Vite2, TypeScript 15 | - [pinia](https://github.com/vuejs/pinia) store 16 | - [Vue Router](https://github.com/vuejs/vue-router) 17 | - [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) - file system based routing 18 | - [`vite-plugin-vue-layouts`](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) - layouts for pages 19 | - [Vue I18n](https://github.com/intlify/vue-i18n-next) - Internationalization 20 | - [`vite-plugin-vue-i18n`](https://github.com/intlify/vite-plugin-vue-i18n) - Vite plugin for Vue I18n 21 | - [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components) - components auto import 22 | 23 | - Tailwind CSS 3.0 w/ following plugins preinstalled: 24 | - `@tailwindcss/aspect-ratio` 25 | - `@tailwindcss/line-clamp` 26 | - `@tailwindcss/typography` 27 | - `@tailwindcss/forms` 28 | - `firefox`-variant 29 | - `daisyui` 30 | - PostCSS 8 w/ `postcss-nesting` plugin and `cssnano` for minimizing production CSS 31 | - Eslint 32 | - Prettier 33 | - Alias `@` to `/src` 34 | - Manually configured global components in `main.ts` 35 | - Predefined and fully typed global variables: 36 | - `VITE_APP_VERSION` is read from `package.json` version at build time 37 | - `VITE_APP_BUILD_EPOCH` is populated as `new Date().getTime()` at build time 38 | - Using newest `script setup` syntax w/ Ref sugar (see the official [Script Setup documentation](https://vuejs.org/api/sfc-script-setup.html) and [Ref Sugar RFC](https://github.com/vuejs/rfcs/discussions/369) discussion) 39 | - Cypress.io e2e tests (configured similarly to `vue-cli`) 40 | - Cypress.io component tests 41 | - GitHub workflows 42 | - Dependabot 43 | - Automated e2e tests 44 | - Automated component tests 45 | - GitLab CI 46 | - Automated e2e tests 47 | - Automated component tests 48 | 49 | ## Thanks 50 | 51 | - [vitesse](https://github.com/antfu/vitesse) 52 | - [vite-ts-tailwind-starter](https://github.com/Uninen/vite-ts-tailwind-starter) 53 | -------------------------------------------------------------------------------- /src/assets/networking-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 13 | 19 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/assets/like-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/assets/club.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/club_with_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------