├── .eslintignore
├── .eslintrc.cjs
├── .gitignore
├── .prettierignore
├── .vscode
└── extensions.json
├── README.md
├── adapters
└── vercel-edge
│ └── vite.config.ts
├── adaptors
└── vercel-edge
│ └── vite.config.ts
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── favicon.svg
├── fonts
│ ├── poppins-400.woff2
│ ├── poppins-500.woff2
│ └── poppins-700.woff2
├── manifest.json
└── robots.txt
├── src
├── components
│ ├── router-head
│ │ └── router-head.tsx
│ └── starter
│ │ ├── counter
│ │ ├── counter.module.css
│ │ └── counter.tsx
│ │ ├── footer
│ │ ├── footer.module.css
│ │ └── footer.tsx
│ │ ├── gauge
│ │ ├── gauge.module.css
│ │ └── index.tsx
│ │ ├── header
│ │ ├── header.module.css
│ │ └── header.tsx
│ │ ├── hero
│ │ ├── hero.module.css
│ │ └── hero.tsx
│ │ ├── icons
│ │ └── qwik.tsx
│ │ ├── infobox
│ │ ├── infobox.module.css
│ │ └── infobox.tsx
│ │ ├── next-steps
│ │ ├── next-steps.module.css
│ │ └── next-steps.tsx
│ │ └── tailwind
│ │ └── tailwind-example.tsx
├── entry.dev.tsx
├── entry.preview.tsx
├── entry.ssr.tsx
├── entry.vercel-edge.tsx
├── global.css
├── root.tsx
└── routes
│ ├── demo
│ ├── flower
│ │ ├── flower.css
│ │ └── index.tsx
│ └── todolist
│ │ ├── index.tsx
│ │ └── todolist.module.css
│ ├── index.tsx
│ ├── layout.tsx
│ ├── service-worker.ts
│ └── styles.css
├── tailwind.config.js
├── tsconfig.json
├── vercel.json
└── vite.config.ts
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.log
2 | **/.DS_Store
3 | *.
4 | .vscode/settings.json
5 | .history
6 | .yarn
7 | bazel-*
8 | bazel-bin
9 | bazel-out
10 | bazel-qwik
11 | bazel-testlogs
12 | dist
13 | dist-dev
14 | lib
15 | lib-types
16 | etc
17 | external
18 | node_modules
19 | temp
20 | tsc-out
21 | tsdoc-metadata.json
22 | target
23 | output
24 | rollup.config.js
25 | build
26 | .cache
27 | .vscode
28 | .rollup.cache
29 | dist
30 | tsconfig.tsbuildinfo
31 | vite.config.ts
32 | *.spec.tsx
33 | *.spec.ts
34 | .netlify
35 | pnpm-lock.yaml
36 | package-lock.json
37 | yarn.lock
38 | server
39 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | es2021: true,
6 | node: true,
7 | },
8 | extends: [
9 | 'eslint:recommended',
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:qwik/recommended',
12 | ],
13 | parser: '@typescript-eslint/parser',
14 | parserOptions: {
15 | tsconfigRootDir: __dirname,
16 | project: ['./tsconfig.json'],
17 | ecmaVersion: 2021,
18 | sourceType: 'module',
19 | ecmaFeatures: {
20 | jsx: true,
21 | },
22 | },
23 | plugins: ['@typescript-eslint'],
24 | rules: {
25 | '@typescript-eslint/no-explicit-any': 'off',
26 | '@typescript-eslint/explicit-module-boundary-types': 'off',
27 | '@typescript-eslint/no-inferrable-types': 'off',
28 | '@typescript-eslint/no-non-null-assertion': 'off',
29 | '@typescript-eslint/no-empty-interface': 'off',
30 | '@typescript-eslint/no-namespace': 'off',
31 | '@typescript-eslint/no-empty-function': 'off',
32 | '@typescript-eslint/no-this-alias': 'off',
33 | '@typescript-eslint/ban-types': 'off',
34 | '@typescript-eslint/ban-ts-comment': 'off',
35 | 'prefer-spread': 'off',
36 | 'no-case-declarations': 'off',
37 | 'no-console': 'off',
38 | '@typescript-eslint/no-unused-vars': ['error'],
39 | '@typescript-eslint/consistent-type-imports': 'warn',
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build
2 | /dist
3 | /lib
4 | /lib-types
5 | /server
6 |
7 | # Development
8 | node_modules
9 | *.local
10 |
11 | # Cache
12 | .cache
13 | .mf
14 | .rollup.cache
15 | tsconfig.tsbuildinfo
16 |
17 | # Logs
18 | logs
19 | *.log
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 | pnpm-debug.log*
24 | lerna-debug.log*
25 |
26 | # Editor
27 | .vscode/*
28 | !.vscode/extensions.json
29 | .idea
30 | .DS_Store
31 | *.suo
32 | *.ntvs*
33 | *.njsproj
34 | *.sln
35 | *.sw?
36 |
37 | # Yarn
38 | .yarn/*
39 | !.yarn/releases
40 |
41 | # Vercel
42 | .vercel
43 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.log
2 | **/.DS_Store
3 | *.
4 | .vscode/settings.json
5 | .history
6 | .yarn
7 | bazel-*
8 | bazel-bin
9 | bazel-out
10 | bazel-qwik
11 | bazel-testlogs
12 | dist
13 | dist-dev
14 | lib
15 | lib-types
16 | etc
17 | external
18 | node_modules
19 | temp
20 | tsc-out
21 | tsdoc-metadata.json
22 | target
23 | output
24 | rollup.config.js
25 | build
26 | .cache
27 | .vscode
28 | .rollup.cache
29 | dist
30 | tsconfig.tsbuildinfo
31 | vite.config.ts
32 | *.spec.tsx
33 | *.spec.ts
34 | .netlify
35 | pnpm-lock.yaml
36 | package-lock.json
37 | yarn.lock
38 | server
39 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["dbaeumer.vscode-eslint", "unifiedjs.vscode-mdx"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Qwik City App ⚡️
2 |
3 | - [Qwik Docs](https://qwik.builder.io/)
4 | - [Discord](https://qwik.builder.io/chat)
5 | - [Qwik GitHub](https://github.com/BuilderIO/qwik)
6 | - [@QwikDev](https://twitter.com/QwikDev)
7 | - [Vite](https://vitejs.dev/)
8 |
9 | ---
10 |
11 | ## Project Structure
12 |
13 | This project is using Qwik with [QwikCity](https://qwik.builder.io/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.
14 |
15 | Inside your project, you'll see the following directory structure:
16 |
17 | ```
18 | ├── public/
19 | │ └── ...
20 | └── src/
21 | ├── components/
22 | │ └── ...
23 | └── routes/
24 | └── ...
25 | ```
26 |
27 | - `src/routes`: Provides the directory based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.builder.io/qwikcity/routing/overview/) for more info.
28 |
29 | - `src/components`: Recommended directory for components.
30 |
31 | - `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info.
32 |
33 | ## Add Integrations and deployment
34 |
35 | Use the `pnpm qwik add` command to add additional integrations. Some examples of integrations include: Cloudflare, Netlify or Express server, and the [Static Site Generator (SSG)](https://qwik.builder.io/qwikcity/guides/static-site-generation/).
36 |
37 | ```shell
38 | pnpm qwik add # or `yarn qwik add`
39 | ```
40 |
41 | ## Development
42 |
43 | Development mode uses [Vite's development server](https://vitejs.dev/). During development, the `dev` command will server-side render (SSR) the output.
44 |
45 | ```shell
46 | npm start # or `yarn start`
47 | ```
48 |
49 | > Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build.
50 |
51 | ## Preview
52 |
53 | The preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to locally preview a production build, and it should not be used as a production server.
54 |
55 | ```shell
56 | pnpm preview # or `yarn preview`
57 | ```
58 |
59 | ## Production
60 |
61 | The production build will generate client and server modules by running both client and server build commands. Additionally, the build command will use Typescript to run a type check on the source code.
62 |
63 | ```shell
64 | pnpm build # or `yarn build`
65 | ```
66 |
67 | ## Vercel Edge
68 |
69 | This starter site is configured to deploy to [Vercel Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions), which means it will be rendered at an edge location near to your users.
70 |
71 | ## Installation
72 |
73 | The adaptor will add a new `vite.config.ts` within the `adapters/` directory, and a new entry file will be created, such as:
74 |
75 | ```
76 | └── adapters/
77 | └── vercel-edge/
78 | └── vite.config.ts
79 | └── src/
80 | └── entry.vercel-edge.tsx
81 | ```
82 |
83 | Additionally, within the `package.json`, the `build.server` script will be updated with the Vercel Edge build.
84 |
85 | ## Production build
86 |
87 | To build the application for production, use the `build` command, this command will automatically run `pnpm build.server` and `pnpm build.client`:
88 |
89 | ```shell
90 | pnpm build
91 | ```
92 |
93 | [Read the full guide here](https://github.com/BuilderIO/qwik/blob/main/starters/adapters/vercel-edge/README.md)
94 |
95 | ## Dev deploy
96 |
97 | To deploy the application for development:
98 |
99 | ```shell
100 | pnpm deploy
101 | ```
102 |
103 | Notice that you might need a [Vercel account](https://docs.Vercel.com/get-started/) in order to complete this step!
104 |
105 | ## Production deploy
106 |
107 | The project is ready to be deployed to Vercel. However, you will need to create a git repository and push the code to it.
108 |
109 | You can [deploy your site to Vercel](https://vercel.com/docs/concepts/deployments/overview) either via a Git provider integration or through the Vercel CLI.
110 |
--------------------------------------------------------------------------------
/adapters/vercel-edge/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { vercelEdgeAdapter } from '@builder.io/qwik-city/adapters/vercel-edge/vite';
2 | import { extendConfig } from '@builder.io/qwik-city/vite';
3 | import baseConfig from '../../vite.config';
4 |
5 | export default extendConfig(baseConfig, () => {
6 | return {
7 | build: {
8 | ssr: true,
9 | rollupOptions: {
10 | input: ['src/entry.vercel-edge.tsx', '@qwik-city-plan'],
11 | },
12 | outDir: '.vercel/output/functions/_qwik-city.func',
13 | },
14 | plugins: [vercelEdgeAdapter()],
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/adaptors/vercel-edge/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { vercelEdgeAdaptor } from '@builder.io/qwik-city/adaptors/vercel-edge/vite';
2 | import { extendConfig } from '@builder.io/qwik-city/vite';
3 | import baseConfig from '../../vite.config';
4 |
5 | export default extendConfig(baseConfig, () => {
6 | return {
7 | build: {
8 | ssr: true,
9 | rollupOptions: {
10 | input: ['src/entry.vercel-edge.tsx', '@qwik-city-plan'],
11 | },
12 | outDir: '.vercel/output/functions/_qwik-city.func',
13 | },
14 | plugins: [
15 | vercelEdgeAdaptor({
16 | staticGenerate: true,
17 | }),
18 | ],
19 | };
20 | });
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-qwik-basic-starter",
3 | "description": "App with Routing built-in (recommended)",
4 | "engines": {
5 | "node": ">=15.0.0"
6 | },
7 | "private": true,
8 | "scripts": {
9 | "build": "qwik build",
10 | "build.client": "vite build",
11 | "build.preview": "vite build --ssr src/entry.preview.tsx",
12 | "build.server": "vite build -c adapters/vercel-edge/vite.config.ts",
13 | "build.types": "tsc --incremental --noEmit",
14 | "deploy": "vercel deploy",
15 | "dev": "vite --mode ssr",
16 | "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
17 | "fmt": "prettier --write .",
18 | "fmt.check": "prettier --check .",
19 | "lint": "eslint \"src/**/*.ts*\"",
20 | "preview": "qwik build preview && vite preview --open",
21 | "start": "vite --open --mode ssr",
22 | "qwik": "qwik"
23 | },
24 | "devDependencies": {
25 | "@builder.io/qwik": "1.0.0",
26 | "@builder.io/qwik-city": "~1.0.0",
27 | "@types/eslint": "8.37.0",
28 | "@types/node": "^18.16.0",
29 | "@typescript-eslint/eslint-plugin": "5.59.1",
30 | "@typescript-eslint/parser": "5.59.1",
31 | "autoprefixer": "^10.4.14",
32 | "eslint": "8.39.0",
33 | "eslint-plugin-qwik": "0.104.0",
34 | "postcss": "^8.4.23",
35 | "prettier": "2.8.8",
36 | "tailwindcss": "^3.3.1",
37 | "typescript": "5.0.4",
38 | "undici": "5.22.0",
39 | "vercel": "^28.19.0",
40 | "vite": "4.3.2",
41 | "vite-tsconfig-paths": "4.2.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/fonts/poppins-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BuilderIO/qwik-tw-vercel-starter-kit/ae4e2e60fbe7ede06703e080da8bdc9f11be3d1a/public/fonts/poppins-400.woff2
--------------------------------------------------------------------------------
/public/fonts/poppins-500.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BuilderIO/qwik-tw-vercel-starter-kit/ae4e2e60fbe7ede06703e080da8bdc9f11be3d1a/public/fonts/poppins-500.woff2
--------------------------------------------------------------------------------
/public/fonts/poppins-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BuilderIO/qwik-tw-vercel-starter-kit/ae4e2e60fbe7ede06703e080da8bdc9f11be3d1a/public/fonts/poppins-700.woff2
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/web-manifest-combined.json",
3 | "name": "qwik-project-name",
4 | "short_name": "Welcome to Qwik",
5 | "start_url": ".",
6 | "display": "standalone",
7 | "background_color": "#fff",
8 | "description": "A Qwik project app."
9 | }
10 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BuilderIO/qwik-tw-vercel-starter-kit/ae4e2e60fbe7ede06703e080da8bdc9f11be3d1a/public/robots.txt
--------------------------------------------------------------------------------
/src/components/router-head/router-head.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import { useDocumentHead, useLocation } from '@builder.io/qwik-city';
3 |
4 | /**
5 | * The RouterHead component is placed inside of the document `
` element.
6 | */
7 | export const RouterHead = component$(() => {
8 | const head = useDocumentHead();
9 | const loc = useLocation();
10 |
11 | return (
12 | <>
13 | {head.title}
14 |
15 |
16 |
17 |
18 |
19 | {head.meta.map((m) => (
20 |
21 | ))}
22 |
23 | {head.links.map((l) => (
24 |
25 | ))}
26 |
27 | {head.styles.map((s) => (
28 |
29 | ))}
30 | >
31 | );
32 | });
33 |
--------------------------------------------------------------------------------
/src/components/starter/counter/counter.module.css:
--------------------------------------------------------------------------------
1 | .counter-wrapper {
2 | margin-top: 50px;
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | gap: 10px;
7 | }
8 |
9 | @media screen and (min-width: 768px) {
10 | .counter-wrapper {
11 | gap: 30px;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/starter/counter/counter.tsx:
--------------------------------------------------------------------------------
1 | import { component$, useSignal, $ } from '@builder.io/qwik';
2 | import styles from './counter.module.css';
3 | import Gauge from '../gauge';
4 |
5 | export default component$(() => {
6 | const count = useSignal(70);
7 |
8 | const setCount = $((newValue: number) => {
9 | if (newValue < 0 || newValue > 100) {
10 | return;
11 | }
12 | count.value = newValue;
13 | });
14 |
15 | return (
16 |
17 | setCount(count.value - 1)}>
18 | -
19 |
20 |
21 | setCount(count.value + 1)}>
22 | +
23 |
24 |
25 | );
26 | });
27 |
--------------------------------------------------------------------------------
/src/components/starter/footer/footer.module.css:
--------------------------------------------------------------------------------
1 | .anchor {
2 | color: white !important;
3 | display: block;
4 | font-size: 0.8rem;
5 | text-align: center;
6 | text-decoration: none;
7 | line-height: 1.5;
8 | }
9 |
10 | .anchor span:not(.spacer) {
11 | display: block;
12 | }
13 |
14 | .spacer {
15 | display: none;
16 | padding: 0 15px;
17 | }
18 |
19 | @media screen and (min-width: 768px) {
20 | .anchor span {
21 | display: inline !important;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/starter/footer/footer.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import { useServerTimeLoader } from '~/routes/layout';
3 | import styles from './footer.module.css';
4 |
5 | export default component$(() => {
6 | const serverTime = useServerTimeLoader();
7 |
8 | return (
9 |
18 | );
19 | });
20 |
--------------------------------------------------------------------------------
/src/components/starter/gauge/gauge.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | position: relative;
3 | }
4 |
5 | .gauge {
6 | width: 160px;
7 | }
8 |
9 | .value {
10 | position: absolute;
11 | top: 50%;
12 | left: 50%;
13 | color: white;
14 | font-size: 3rem;
15 | transform: translate(-50%, -50%);
16 | width: 200px;
17 | text-align: center;
18 | }
19 |
20 | @media screen and (min-width: 768px) {
21 | .gauge {
22 | width: 400px;
23 | }
24 | .value {
25 | font-size: 7rem;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/starter/gauge/index.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import styles from './gauge.module.css';
3 |
4 | export default component$(({ value = 50 }: { value?: number }) => {
5 | const safeValue = value < 0 || value > 100 ? 50 : value;
6 |
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 | {safeValue}
30 |
31 | );
32 | });
33 |
--------------------------------------------------------------------------------
/src/components/starter/header/header.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | align-items: center;
4 | justify-content: space-between;
5 | }
6 |
7 | .logo {
8 | display: inline-block;
9 | }
10 | .logo a {
11 | display: block;
12 | }
13 |
14 | .header ul {
15 | margin: 0;
16 | padding: 0;
17 | list-style: none;
18 | display: flex;
19 | gap: 30px;
20 | }
21 |
22 | .header li {
23 | display: none;
24 | margin: 0;
25 | padding: 0;
26 | font-size: 0.7rem;
27 | }
28 |
29 | .header li a {
30 | color: white;
31 | display: inline-block;
32 | padding: 0;
33 | text-decoration: none;
34 | }
35 |
36 | .header li a:hover {
37 | color: var(--qwik-light-blue);
38 | }
39 |
40 | @media (min-width: 450px) {
41 | .header li {
42 | display: inline-block;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/starter/header/header.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import { QwikLogo } from '../icons/qwik';
3 | import styles from './header.module.css';
4 |
5 | export default component$(() => {
6 | return (
7 |
33 | );
34 | });
35 |
--------------------------------------------------------------------------------
/src/components/starter/hero/hero.module.css:
--------------------------------------------------------------------------------
1 | .hero {
2 | display: flex;
3 | vertical-align: middle;
4 | flex-direction: column;
5 | flex-wrap: nowrap;
6 | align-items: center;
7 | height: 450px;
8 | justify-content: center;
9 | gap: 40px;
10 | }
11 |
12 | .hero p {
13 | color: white;
14 | margin: 0;
15 | font-size: 1rem;
16 | }
17 |
18 | .button-group {
19 | display: flex;
20 | flex-direction: row;
21 | gap: 24px;
22 | }
23 |
24 | @media screen and (min-width: 768px) {
25 | .hero {
26 | gap: 60px;
27 | height: 500px;
28 | }
29 |
30 | .hero p {
31 | font-size: 1.3rem;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/starter/hero/hero.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import styles from './hero.module.css';
3 |
4 | export default component$(() => {
5 | return (
6 |
7 |
8 | So fantastic
9 |
10 | to have you here
11 |
12 |
Have fun building your App with Qwik.
13 |
78 |
79 | );
80 | });
81 |
--------------------------------------------------------------------------------
/src/components/starter/icons/qwik.tsx:
--------------------------------------------------------------------------------
1 | export const QwikLogo = ({ width = 100, height = 35 }: { width?: number; height?: number }) => (
2 |
9 |
13 |
17 |
21 |
25 |
29 |
33 |
37 |
38 | );
39 |
--------------------------------------------------------------------------------
/src/components/starter/infobox/infobox.module.css:
--------------------------------------------------------------------------------
1 | .infobox {
2 | color: white;
3 | font-size: 0.8rem;
4 | line-height: 2;
5 | margin: 0 0 40px;
6 | }
7 |
8 | .infobox h3 {
9 | font-size: 1rem;
10 | font-weight: 400;
11 | margin: 0 0 15px;
12 | padding: 0;
13 | }
14 |
15 | .infobox li {
16 | line-height: 2.5;
17 | }
18 |
19 | @media screen and (min-width: 600px) {
20 | .infobox {
21 | margin: 0;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/starter/infobox/infobox.tsx:
--------------------------------------------------------------------------------
1 | import { Slot, component$ } from '@builder.io/qwik';
2 | import styles from './infobox.module.css';
3 |
4 | export default component$(() => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 | );
13 | });
14 |
--------------------------------------------------------------------------------
/src/components/starter/next-steps/next-steps.module.css:
--------------------------------------------------------------------------------
1 | .gettingstarted {
2 | display: flex;
3 | color: white;
4 | flex-direction: column;
5 | justify-content: center;
6 | align-items: center;
7 | height: 280px;
8 | line-height: 1.5;
9 | gap: 10px;
10 | max-width: 600px;
11 | margin: 0 auto;
12 | }
13 |
14 | .gettingstarted .intro {
15 | font-size: 1rem;
16 | width: 100%;
17 | word-break: break-word;
18 | }
19 | .gettingstarted .hint {
20 | font-size: 0.8rem;
21 | }
22 | .gettingstarted .hint a {
23 | color: var(--qwik-dark-purple);
24 | }
25 |
26 | @media screen and (min-width: 768px) {
27 | .gettingstarted {
28 | height: 180px;
29 | }
30 | .gettingstarted .intro {
31 | font-size: 1.2rem;
32 | }
33 | .gettingstarted .hint {
34 | font-size: 1rem;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/starter/next-steps/next-steps.tsx:
--------------------------------------------------------------------------------
1 | import { component$, $, useOnWindow, useSignal } from '@builder.io/qwik';
2 | import styles from './next-steps.module.css';
3 |
4 | export const GETTING_STARTED_STEPS = [
5 | {
6 | message: "Press and hold the ALT key to active 'Click-to-Source' mode",
7 | },
8 | {
9 | message: 'Select the title of this page while keeping the ALT key pressed',
10 | hint: 'Edit the title and save the changes. If your editor does not open, have a look at this page to set the correct LAUNCH_EDITOR
value.',
11 | },
12 | {
13 | message:
14 | 'Update now the routeLoader$
defined in the src/routes//layout.tsx
file',
15 | hint: 'Instead of returning the current date, you could return any possible string. The output is displayed in the footer.',
16 | },
17 | {
18 | message: 'Create a new Route called /me
',
19 | hint: 'Create a new directory called me
in src/routes
. Within this directory create a index.tsx
file or copy the src/routes/index.tsx
file. Your new route is now accessible here ✨',
20 | },
21 | {
22 | message: 'Time to have a look at Forms ',
23 | hint: 'Open the TODO list App and add some items to the list. Try the same with disabled JavaScript 🐰',
24 | },
25 | {
26 | message: 'Congratulations! You are now familiar with the basics! 🎉',
27 | hint: "If you need further info on how to use qwik, have a look at qwik.builder.io or join the Discord channel .",
28 | },
29 | ];
30 |
31 | export default component$(() => {
32 | const gettingStartedStep = useSignal(0);
33 |
34 | useOnWindow(
35 | 'keydown',
36 | $((e) => {
37 | if ((e as KeyboardEvent).key === 'Alt') {
38 | gettingStartedStep.value = 1;
39 | }
40 | })
41 | );
42 |
43 | return (
44 |
45 |
46 | Time for a quick
47 |
48 | qwik intro ?
49 |
50 |
60 | {gettingStartedStep.value + 1 < GETTING_STARTED_STEPS.length ? (
61 |
gettingStartedStep.value++}>
62 | Continue with Step {gettingStartedStep.value + 2} of {GETTING_STARTED_STEPS.length}
63 |
64 | ) : (
65 |
(gettingStartedStep.value = 0)}>
66 | Re-Start
67 |
68 | )}
69 |
70 | );
71 | });
72 |
--------------------------------------------------------------------------------
/src/components/starter/tailwind/tailwind-example.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 |
3 | export const TailwindExample = component$(() => {
4 | return (
5 | <>
6 | Look below 👇🏽, It's'a Tailwind Example!
7 |
8 |
9 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Boost your productivity.
31 |
32 | Start using Qwik today.
33 |
34 |
35 | Ac euismod vel sit maecenas id pellentesque eu sed consectetur.
36 | Malesuada adipiscing sagittis vel nulla.
37 |
38 |
49 |
50 |
51 |
58 |
59 |
60 |
61 | >
62 | );
63 | });
64 |
--------------------------------------------------------------------------------
/src/entry.dev.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * WHAT IS THIS FILE?
3 | *
4 | * Development entry point using only client-side modules:
5 | * - Do not use this mode in production!
6 | * - No SSR
7 | * - No portion of the application is pre-rendered on the server.
8 | * - All of the application is running eagerly in the browser.
9 | * - More code is transferred to the browser than in SSR mode.
10 | * - Optimizer/Serialization/Deserialization code is not exercised!
11 | */
12 | import { render, type RenderOptions } from '@builder.io/qwik';
13 | import Root from './root';
14 |
15 | export default function (opts: RenderOptions) {
16 | return render(document, , opts);
17 | }
18 |
--------------------------------------------------------------------------------
/src/entry.preview.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * WHAT IS THIS FILE?
3 | *
4 | * It's the bundle entry point for `npm run preview`.
5 | * That is, serving your app built in production mode.
6 | *
7 | * Feel free to modify this file, but don't remove it!
8 | *
9 | * Learn more about Vite's preview command:
10 | * - https://vitejs.dev/config/preview-options.html#preview-options
11 | *
12 | */
13 | import { createQwikCity } from '@builder.io/qwik-city/middleware/node';
14 | import qwikCityPlan from '@qwik-city-plan';
15 | import render from './entry.ssr';
16 |
17 | /**
18 | * The default export is the QwikCity adapter used by Vite preview.
19 | */
20 | export default createQwikCity({ render, qwikCityPlan });
21 |
--------------------------------------------------------------------------------
/src/entry.ssr.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * WHAT IS THIS FILE?
3 | *
4 | * SSR entry point, in all cases the application is render outside the browser, this
5 | * entry point will be the common one.
6 | *
7 | * - Server (express, cloudflare...)
8 | * - npm run start
9 | * - npm run preview
10 | * - npm run build
11 | *
12 | */
13 | import { renderToStream, type RenderToStreamOptions } from '@builder.io/qwik/server';
14 | import { manifest } from '@qwik-client-manifest';
15 | import Root from './root';
16 |
17 | export default function (opts: RenderToStreamOptions) {
18 | return renderToStream( , {
19 | manifest,
20 | ...opts,
21 | // Use container attributes to set attributes on the html tag.
22 | containerAttributes: {
23 | lang: 'en-us',
24 | ...opts.containerAttributes,
25 | },
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/src/entry.vercel-edge.tsx:
--------------------------------------------------------------------------------
1 | import { createQwikCity, type PlatformVercel } from '@builder.io/qwik-city/middleware/vercel-edge';
2 | import qwikCityPlan from '@qwik-city-plan';
3 | import { manifest } from '@qwik-client-manifest';
4 | import render from './entry.ssr';
5 |
6 | declare global {
7 | interface QwikCityPlatform extends PlatformVercel {}
8 | }
9 |
10 | export default createQwikCity({ render, qwikCityPlan, manifest });
11 |
--------------------------------------------------------------------------------
/src/global.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Tailwind CSS imports
3 | * View the full documentation at https://tailwindcss.com
4 | */
5 | @tailwind base;
6 | @tailwind components;
7 | @tailwind utilities;
8 |
9 | @layer base {
10 | :root {
11 | --qwik-dark-blue: #006ce9;
12 | --qwik-light-blue: #18b6f6;
13 | --qwik-light-purple: #ac7ff4;
14 | --qwik-dark-purple: #713fc2;
15 | --qwik-dirty-black: #1d2033;
16 | --qwik-dark-background: #151934;
17 | --qwik-dark-text: #ffffff;
18 | }
19 | }
20 |
21 | /**
22 | * WHAT IS THIS FILE?
23 | *
24 | * Globally applied styles. No matter which components are in the page or matching route,
25 | * the styles in here will be applied to the Document, without any sort of CSS scoping.
26 | *
27 | */
28 |
29 | /**
30 | * WHAT IS THIS FILE?
31 | *
32 | * Globally applied styles. No matter which components are in the page or matching route,
33 | * the styles in here will be applied to the Document, without any sort of CSS scoping.
34 | *
35 | */
36 |
--------------------------------------------------------------------------------
/src/root.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
3 | import { RouterHead } from './components/router-head/router-head';
4 |
5 | import './global.css';
6 |
7 | export default component$(() => {
8 | /**
9 | * The root of a QwikCity site always start with the component,
10 | * immediately followed by the document's and .
11 | *
12 | * Dont remove the `` and `` elements.
13 | */
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 | });
29 |
--------------------------------------------------------------------------------
/src/routes/demo/flower/flower.css:
--------------------------------------------------------------------------------
1 | .host {
2 | display: grid;
3 |
4 | align-items: center;
5 | justify-content: center;
6 | justify-items: center;
7 | --rotation: 135deg;
8 | --rotation: 225deg;
9 | --size-step: 10px;
10 | --odd-color-step: 5;
11 | --even-color-step: 5;
12 | --center: 12;
13 |
14 | width: 100%;
15 | height: 500px;
16 |
17 | contain: strict;
18 | }
19 |
20 | h1 {
21 | margin-bottom: 60px;
22 | }
23 |
24 | .input {
25 | width: 60%;
26 | }
27 |
28 | .square {
29 | --size: calc(40px + var(--index) * var(--size-step));
30 |
31 | display: block;
32 | width: var(--size);
33 | height: var(--size);
34 | transform: rotateZ(calc(var(--rotation) * var(--state) * (var(--center) - var(--index))));
35 | transition-property: transform, border-color;
36 | transition-duration: 5s;
37 | transition-timing-function: ease-in-out;
38 | grid-area: 1 / 1;
39 | background: white;
40 | border-width: 2px;
41 | border-style: solid;
42 | border-color: black;
43 | box-sizing: border-box;
44 | will-change: transform, border-color;
45 |
46 | contain: strict;
47 | }
48 |
49 | .square.odd {
50 | --luminance: calc(1 - calc(calc(var(--index) * var(--odd-color-step)) / 256));
51 | background: rgb(
52 | calc(172 * var(--luminance)),
53 | calc(127 * var(--luminance)),
54 | calc(244 * var(--luminance))
55 | );
56 | }
57 |
58 | .pride .square:nth-child(12n + 1) {
59 | background: #e70000;
60 | }
61 | .pride .square:nth-child(12n + 3) {
62 | background: #ff8c00;
63 | }
64 | .pride .square:nth-child(12n + 5) {
65 | background: #ffef00;
66 | }
67 | .pride .square:nth-child(12n + 7) {
68 | background: #00811f;
69 | }
70 | .pride .square:nth-child(12n + 9) {
71 | background: #0044ff;
72 | }
73 | .pride .square:nth-child(12n + 11) {
74 | background: #760089;
75 | }
76 |
--------------------------------------------------------------------------------
/src/routes/demo/flower/index.tsx:
--------------------------------------------------------------------------------
1 | import { component$, useVisibleTask$, useStore, useStylesScoped$ } from '@builder.io/qwik';
2 | import { type DocumentHead, useLocation } from '@builder.io/qwik-city';
3 | import styles from './flower.css?inline';
4 |
5 | export default component$(() => {
6 | useStylesScoped$(styles);
7 | const loc = useLocation();
8 |
9 | const state = useStore({
10 | count: 0,
11 | number: 20,
12 | });
13 |
14 | useVisibleTask$(({ cleanup }) => {
15 | const timeout = setTimeout(() => (state.count = 1), 500);
16 | cleanup(() => clearTimeout(timeout));
17 |
18 | const internal = setInterval(() => state.count++, 7000);
19 | cleanup(() => clearInterval(internal));
20 | });
21 |
22 | return (
23 |
24 |
25 |
26 | Generate Flowers
27 |
28 |
29 |
{
35 | state.number = (ev.target as HTMLInputElement).valueAsNumber;
36 | }}
37 | />
38 |
47 | {Array.from({ length: state.number }, (_, i) => (
48 |
56 | )).reverse()}
57 |
58 |
59 | );
60 | });
61 |
62 | export const head: DocumentHead = {
63 | title: 'Qwik Flower',
64 | };
65 |
--------------------------------------------------------------------------------
/src/routes/demo/todolist/index.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import {
3 | type DocumentHead,
4 | routeLoader$,
5 | routeAction$,
6 | zod$,
7 | z,
8 | Form,
9 | } from '@builder.io/qwik-city';
10 | import styles from './todolist.module.css';
11 |
12 | interface ListItem {
13 | text: string;
14 | }
15 |
16 | export const list: ListItem[] = [];
17 |
18 | export const useListLoader = routeLoader$(() => {
19 | return list;
20 | });
21 |
22 | export const useAddToListAction = routeAction$(
23 | (item) => {
24 | list.push(item);
25 | return {
26 | success: true,
27 | };
28 | },
29 | zod$({
30 | text: z.string().trim().min(1),
31 | })
32 | );
33 |
34 | export default component$(() => {
35 | const list = useListLoader();
36 | const action = useAddToListAction();
37 |
38 | return (
39 | <>
40 |
41 |
42 | TODO List
43 |
44 |
45 |
46 |
47 |
48 |
49 | {(list.value.length && (
50 |
51 | {list.value.map((item, index) => (
52 | {item.text}
53 | ))}
54 |
55 | )) ||
No items found }
56 |
57 |
58 |
59 |
65 |
66 |
PS: This little app works even when JavaScript is disabled.
67 |
68 | >
69 | );
70 | });
71 |
72 | export const head: DocumentHead = {
73 | title: 'Qwik Todo List',
74 | };
75 |
--------------------------------------------------------------------------------
/src/routes/demo/todolist/todolist.module.css:
--------------------------------------------------------------------------------
1 | .list {
2 | display: flex;
3 | flex-direction: column;
4 | gap: 20px;
5 | color: white;
6 | }
7 |
8 | .list,
9 | .empty {
10 | min-height: 250px;
11 | }
12 |
13 | .list li {
14 | list-style: none;
15 | }
16 |
17 | .empty {
18 | color: white;
19 | display: block;
20 | }
21 |
22 | .input {
23 | background: white;
24 | color: var(--qwik-light-blue);
25 | border: none;
26 | border-radius: 8px;
27 | padding: 15px 20px;
28 | margin-right: 10px;
29 | font-size: 0.8rem;
30 | }
31 |
32 | .hint {
33 | font-size: 0.8rem;
34 | color: white;
35 | margin-top: 30px;
36 | }
37 |
38 | @media screen and (min-width: 768px) {
39 | .input {
40 | padding: 23px 35px;
41 | margin-right: 20px;
42 | font-size: 1rem;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import type { DocumentHead } from '@builder.io/qwik-city';
3 |
4 | import Counter from '~/components/starter/counter/counter';
5 | import Hero from '~/components/starter/hero/hero';
6 | import Infobox from '~/components/starter/infobox/infobox';
7 | import Starter from '~/components/starter/next-steps/next-steps';
8 | import { TailwindExample } from '~/components/starter/tailwind/tailwind-example';
9 |
10 | export default component$(() => {
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | You can count
23 | on me
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | CLI Commands
32 |
33 | <>
34 |
35 | npm run dev
36 |
37 | Starts the development server and watches for changes
38 |
39 |
40 | npm run preview
41 |
42 | Creates production build and starts a server to preview it
43 |
44 |
45 | npm run build
46 |
47 | Creates production build
48 |
49 |
50 | npm run qwik add
51 |
52 | Runs the qwik CLI to add integrations
53 |
54 | >
55 |
56 |
57 |
58 |
59 |
60 | Example Apps
61 |
62 |
63 | Have a look at the Flower App or the{' '}
64 | Todo App .
65 |
66 |
67 |
68 |
69 |
72 |
99 |
100 |
101 |
102 | >
103 | );
104 | });
105 |
106 | export const head: DocumentHead = {
107 | title: 'Welcome to Qwik',
108 | meta: [
109 | {
110 | name: 'description',
111 | content: 'Qwik site description',
112 | },
113 | ],
114 | };
115 |
--------------------------------------------------------------------------------
/src/routes/layout.tsx:
--------------------------------------------------------------------------------
1 | import { component$, Slot, useStyles$ } from '@builder.io/qwik';
2 | import { routeLoader$ } from '@builder.io/qwik-city';
3 |
4 | import Header from '~/components/starter/header/header';
5 | import Footer from '~/components/starter/footer/footer';
6 |
7 | import styles from './styles.css?inline';
8 |
9 | export const useServerTimeLoader = routeLoader$(() => {
10 | return {
11 | date: new Date().toISOString(),
12 | };
13 | });
14 |
15 | export default component$(() => {
16 | useStyles$(styles);
17 | return (
18 | <>
19 |
20 |
21 |
22 |
23 |
24 | >
25 | );
26 | });
27 |
--------------------------------------------------------------------------------
/src/routes/service-worker.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * WHAT IS THIS FILE?
3 | *
4 | * The service-worker.ts file is used to have state of the art prefetching.
5 | * https://qwik.builder.io/qwikcity/prefetching/overview/
6 | *
7 | * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline.
8 | * You can also use this file to add more functionality that runs in the service worker.
9 | */
10 | import { setupServiceWorker } from '@builder.io/qwik-city/service-worker';
11 |
12 | setupServiceWorker();
13 |
14 | addEventListener('install', () => self.skipWaiting());
15 |
16 | addEventListener('activate', () => self.clients.claim());
17 |
18 | declare const self: ServiceWorkerGlobalScope;
19 |
--------------------------------------------------------------------------------
/src/routes/styles.css:
--------------------------------------------------------------------------------
1 | /* THIS FILE IS JUST FOR EXAMPLES, DELETE IT IF YOU DON'T NEED IT */
2 |
3 | /* SHELL ---------------------------------------- */
4 | html {
5 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
6 | 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
7 | 'Segoe UI Symbol', 'Noto Color Emoji';
8 | }
9 |
10 | body {
11 | background: var(--qwik-dark-background);
12 | color: var(--qwik-dark-text);
13 | overflow-x: hidden;
14 | }
15 |
16 | /* HEADINGS ------------------------------------- */
17 | h1,
18 | h2,
19 | h3 {
20 | color: white;
21 | margin: 0;
22 | }
23 |
24 | h1 {
25 | font-size: 3.2rem;
26 | text-align: center;
27 | }
28 | h1 .highlight,
29 | h3 .highlight {
30 | color: var(--qwik-light-blue);
31 | }
32 |
33 | h2 {
34 | font-weight: 400;
35 | font-size: 2.4rem;
36 | }
37 | h2 .highlight {
38 | font-weight: 700;
39 | }
40 |
41 | h3 {
42 | font-size: 2rem;
43 | }
44 |
45 | @media screen and (min-width: 768px) {
46 | h1 {
47 | font-size: 5rem;
48 | }
49 | h2 {
50 | font-size: 3.4rem;
51 | }
52 | h3 {
53 | font-size: 3rem;
54 | }
55 | }
56 |
57 | /* TAGS ----------------------------------------- */
58 | a {
59 | text-decoration: none;
60 | color: var(--qwik-light-blue);
61 | }
62 |
63 | code {
64 | background: rgba(230, 230, 230, 0.3);
65 | border-radius: 4px;
66 | padding: 2px 6px;
67 | }
68 |
69 | ul {
70 | margin: 0;
71 | padding-left: 20px;
72 | }
73 |
74 | /* CONTAINER ------------------------------------ */
75 | .container {
76 | margin: 0 auto;
77 | padding: 30px 40px;
78 | }
79 | .container.container-purple {
80 | background: var(--qwik-light-purple);
81 | }
82 | .container.container-dark {
83 | background: var(--qwik-dark-background);
84 | color: var(--qwik-dark-text);
85 | }
86 | .container.container-center {
87 | text-align: center;
88 | }
89 | .container.container-flex {
90 | /* does nothing on mobile */
91 | }
92 | .container.container-spacing-xl {
93 | padding: 50px 40px;
94 | }
95 |
96 | @media screen and (min-width: 768px) {
97 | .container {
98 | padding: 50px 80px;
99 | }
100 | .container.container-spacing-xl {
101 | padding: 100px 60px;
102 | }
103 | .container.container-flex {
104 | display: flex;
105 | justify-content: center;
106 | gap: 60px;
107 | }
108 | }
109 |
110 | /* BUTTONS -------------------------------------- */
111 | a.button,
112 | button {
113 | background: var(--qwik-light-blue);
114 | border: none;
115 | border-radius: 8px;
116 | color: white;
117 | cursor: pointer;
118 | font-size: 0.8rem;
119 | padding: 15px 20px;
120 | text-align: center;
121 | }
122 |
123 | a.button.button-dark,
124 | button.button-dark {
125 | background: var(--qwik-dirty-black);
126 | }
127 |
128 | a.button.button-small,
129 | button.button-small {
130 | padding: 15px 25px;
131 | }
132 |
133 | @media screen and (min-width: 768px) {
134 | a.button,
135 | button {
136 | font-size: 1rem;
137 | padding: 23px 35px;
138 | }
139 | }
140 |
141 | /* DESIGN --------------------------------------- */
142 | .ellipsis {
143 | position: absolute;
144 | top: 100px;
145 | left: -100px;
146 | width: 400px;
147 | height: 400px;
148 | background: radial-gradient(
149 | 57.58% 57.58% at 48.79% 42.42%,
150 | rgba(24, 180, 244, 0.5) 0%,
151 | rgba(46, 55, 114, 0) 63.22%
152 | );
153 | transform: rotate(5deg);
154 | opacity: 0.5;
155 | z-index: -1;
156 | }
157 | .ellipsis.ellipsis-purple {
158 | top: 1350px;
159 | left: -100px;
160 | background: radial-gradient(
161 | 50% 50% at 50% 50%,
162 | rgba(172, 127, 244, 0.5) 0%,
163 | rgba(21, 25, 52, 0) 100%
164 | );
165 | transform: rotate(-5deg);
166 | }
167 |
168 | @media screen and (min-width: 768px) {
169 | .ellipsis {
170 | top: -100px;
171 | left: 350px;
172 | width: 1400px;
173 | height: 800px;
174 | }
175 | .ellipsis.ellipsis-purple {
176 | top: 1300px;
177 | left: -200px;
178 | }
179 | }
180 |
181 | /* used icon pack: https://www.svgrepo.com/collection/phosphor-thin-icons */
182 | .icon:before {
183 | width: 18px;
184 | height: 18px;
185 | content: '';
186 | display: inline-block;
187 | margin-right: 20px;
188 | position: relative;
189 | top: 2px;
190 | }
191 |
192 | .icon-cli:before {
193 | background-image: url("data:image/svg+xml,%3Csvg fill='%23ffffff' width='20px' height='20px' viewBox='0 0 256 256' id='Flat' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M122.499 124.87646a4.00053 4.00053 0 0 1 0 6.24708l-40 32a4.0002 4.0002 0 0 1-4.998-6.24708L113.59668 128 77.501 99.12354a4.0002 4.0002 0 0 1 4.998-6.24708ZM175.99414 156h-40a4 4 0 0 0 0 8h40a4 4 0 1 0 0-8ZM228 56.48535v143.0293A12.49909 12.49909 0 0 1 215.51465 212H40.48535A12.49909 12.49909 0 0 1 28 199.51465V56.48535A12.49909 12.49909 0 0 1 40.48535 44h175.0293A12.49909 12.49909 0 0 1 228 56.48535Zm-8 0A4.49023 4.49023 0 0 0 215.51465 52H40.48535A4.49023 4.49023 0 0 0 36 56.48535v143.0293A4.49023 4.49023 0 0 0 40.48535 204h175.0293A4.49023 4.49023 0 0 0 220 199.51465Z'/%3E%3C/svg%3E");
194 | }
195 |
196 | .icon-apps:before {
197 | background-image: url("data:image/svg+xml,%3Csvg fill='%23ffffff' width='20px' height='20px' viewBox='0 0 256 256' id='Flat' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M216 44.00586H40a12.01375 12.01375 0 0 0-12 12v144a12.01375 12.01375 0 0 0 12 12H216a12.01375 12.01375 0 0 0 12-12v-144A12.01375 12.01375 0 0 0 216 44.00586Zm4 156a4.00458 4.00458 0 0 1-4 4H40a4.00458 4.00458 0 0 1-4-4v-144a4.00458 4.00458 0 0 1 4-4H216a4.00458 4.00458 0 0 1 4 4Zm-144-116a8 8 0 1 1-8-8A7.99977 7.99977 0 0 1 76 84.00586Zm40 0a8 8 0 1 1-8-8A7.99977 7.99977 0 0 1 116 84.00586Z'/%3E%3C/svg%3E");
198 | }
199 |
200 | .icon-community:before {
201 | background-image: url("data:image/svg+xml,%3Csvg fill='%23ffffff' width='20px' height='20px' viewBox='0 0 256 256' id='Flat' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M246.40381 143.19434a4.00061 4.00061 0 0 1-5.60108-.7959A55.57857 55.57857 0 0 0 196 120a4 4 0 0 1 0-8 28 28 0 1 0-27.50732-33.26074 4.00013 4.00013 0 0 1-7.85987-1.49219 36.00191 36.00191 0 1 1 54.06494 37.50513 63.58068 63.58068 0 0 1 32.50147 22.84155A3.99993 3.99993 0 0 1 246.40381 143.19434Zm-57.24268 71.05273a3.9998 3.9998 0 1 1-7.1914 3.50391 60.02582 60.02582 0 0 0-107.93946 0 3.9998 3.9998 0 1 1-7.1914-3.50391 67.56008 67.56008 0 0 1 40.90625-35.20581 44 44 0 1 1 40.50976 0A67.56139 67.56139 0 0 1 189.16113 214.24707ZM128 176a36 36 0 1 0-36-36A36.04061 36.04061 0 0 0 128 176ZM60 112A28 28 0 1 1 87.50732 78.73828a3.99989 3.99989 0 1 0 7.85938-1.49219A36.00177 36.00177 0 1 0 41.30225 114.7522 63.5829 63.5829 0 0 0 8.79883 137.5957a4 4 0 1 0 6.39648 4.80469A55.58072 55.58072 0 0 1 60 120a4 4 0 0 0 0-8Z'/%3E%3C/svg%3E");
202 | }
203 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | };
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "target": "ES2017",
5 | "module": "ES2020",
6 | "lib": ["es2020", "DOM", "WebWorker", "DOM.Iterable"],
7 | "jsx": "react-jsx",
8 | "jsxImportSource": "@builder.io/qwik",
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "resolveJsonModule": true,
12 | "moduleResolution": "node",
13 | "esModuleInterop": true,
14 | "skipLibCheck": true,
15 | "incremental": true,
16 | "isolatedModules": true,
17 | "outDir": "tmp",
18 | "noEmit": true,
19 | "types": ["node", "vite/client"],
20 | "paths": {
21 | "~/*": ["./src/*"]
22 | }
23 | },
24 | "files": ["./.eslintrc.cjs"],
25 | "include": ["src"]
26 | }
27 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "headers": [
3 | {
4 | "source": "/build/(.*)",
5 | "headers": [
6 | {
7 | "key": "Cache-Control",
8 | "value": "public, max-age=31536000, s-maxage=31536000, immutable"
9 | }
10 | ]
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import { qwikVite } from '@builder.io/qwik/optimizer';
3 | import { qwikCity } from '@builder.io/qwik-city/vite';
4 | import tsconfigPaths from 'vite-tsconfig-paths';
5 |
6 | export default defineConfig(() => {
7 | return {
8 | plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
9 | preview: {
10 | headers: {
11 | 'Cache-Control': 'public, max-age=600',
12 | },
13 | },
14 | };
15 | });
16 |
--------------------------------------------------------------------------------