├── public
├── robots.txt
├── manifest.json
└── favicon.svg
├── .gitignore
├── postcss.config.js
├── tailwind.config.js
├── src
├── components
│ ├── footer
│ │ ├── footer.module.css
│ │ └── footer.tsx
│ ├── header
│ │ ├── header.module.css
│ │ └── header.tsx
│ ├── router-head
│ │ └── router-head.tsx
│ └── icons
│ │ └── qwik.tsx
├── entry.dev.tsx
├── entry.preview.tsx
├── routes
│ ├── layout.tsx
│ ├── service-worker.ts
│ ├── index.tsx
│ └── modular-forms
│ │ └── index.tsx
├── entry.ssr.tsx
├── root.tsx
└── global.css
├── vite.config.ts
├── tsconfig.json
├── .vscode
└── settings.json
├── package.json
└── README.md
/public/robots.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .history
3 | /server
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/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 | }
8 |
9 | .spacer {
10 | padding: 0 15px;
11 | }
12 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/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 |
16 | );
17 | });
18 |
--------------------------------------------------------------------------------
/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 render from './entry.ssr';
15 | import qwikCityPlan from '@qwik-city-plan';
16 |
17 | /**
18 | * The default export is the QwikCity adapter used by Vite preview.
19 | */
20 | export default createQwikCity({ render, qwikCityPlan });
21 |
--------------------------------------------------------------------------------
/src/routes/layout.tsx:
--------------------------------------------------------------------------------
1 | import { component$, Slot } from '@builder.io/qwik';
2 | import { routeLoader$ } from '@builder.io/qwik-city';
3 |
4 | import Header from '~/components/header/header';
5 | import Footer from '~/components/footer/footer';
6 |
7 | export const useServerTimeLoader = routeLoader$(() => {
8 | return {
9 | date: new Date().toISOString(),
10 | };
11 | });
12 |
13 | export default component$(() => {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
25 |
26 | );
27 | });
28 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/header/header.module.css:
--------------------------------------------------------------------------------
1 | .header {
2 | display: flex;
3 | align-items: center;
4 | padding: 20px 10px 0;
5 | }
6 |
7 | .header .logo a {
8 | display: inline-block;
9 | }
10 |
11 | .header ul {
12 | margin: 0;
13 | list-style: none;
14 | flex: 1;
15 | text-align: right;
16 | }
17 |
18 | .header li {
19 | display: none;
20 | margin: 0;
21 | padding: 0;
22 | }
23 |
24 | .header li a {
25 | color: white;
26 | display: inline-block;
27 | padding: 0 1em;
28 | text-decoration: none;
29 | }
30 |
31 | .header li a:hover {
32 | color: var(--qwik-dark-purple);
33 | }
34 |
35 | @media (min-width: 450px) {
36 | .header li {
37 | display: inline-block;
38 | }
39 | }
40 |
41 | @media (min-width: 768px) {
42 | .header {
43 | padding: 20px 70px 0;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.colorCustomizations": {
3 | "activityBar.activeBackground": "#ab307e",
4 | "activityBar.background": "#ab307e",
5 | "activityBar.foreground": "#e7e7e7",
6 | "activityBar.inactiveForeground": "#e7e7e799",
7 | "activityBarBadge.background": "#25320e",
8 | "activityBarBadge.foreground": "#e7e7e7",
9 | "commandCenter.border": "#e7e7e799",
10 | "sash.hoverBorder": "#ab307e",
11 | "statusBar.background": "#5b1a44",
12 | "statusBar.border": "#5b1a44",
13 | "statusBar.foreground": "#e7e7e7",
14 | "statusBarItem.hoverBackground": "#832562",
15 | "statusBarItem.remoteBackground": "#5b1a44",
16 | "statusBarItem.remoteForeground": "#e7e7e7",
17 | "tab.activeBorder": "#ab307e",
18 | "titleBar.activeBackground": "#ab307e",
19 | "titleBar.activeForeground": "#e7e7e7",
20 | "titleBar.border": "#ab307e",
21 | "titleBar.inactiveBackground": "#ab307e99",
22 | "titleBar.inactiveForeground": "#e7e7e799"
23 | },
24 | "peacock.color": "#832561"
25 | }
--------------------------------------------------------------------------------
/src/components/header/header.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import { QwikLogo } from '../icons/qwik';
3 |
4 | import styles from './header.module.css';
5 |
6 | export default component$(() => {
7 | return (
8 |
44 | );
45 | });
46 |
--------------------------------------------------------------------------------
/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.types": "tsc --incremental --noEmit",
13 | "deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
14 | "dev": "vite --mode ssr",
15 | "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
16 | "fmt": "prettier --write .",
17 | "fmt.check": "prettier --check .",
18 | "lint": "eslint \"src/**/*.ts*\"",
19 | "preview": "qwik build preview && vite preview --open",
20 | "start": "vite --open --mode ssr",
21 | "qwik": "qwik"
22 | },
23 | "devDependencies": {
24 | "@types/eslint": "8.21.3",
25 | "@types/node": "^18.15.3",
26 | "@typescript-eslint/eslint-plugin": "5.56.0",
27 | "@typescript-eslint/parser": "5.56.0",
28 | "autoprefixer": "^10.4.13",
29 | "eslint": "8.36.0",
30 | "eslint-plugin-qwik": "0.24.0",
31 | "postcss": "^8.4.16",
32 | "prettier": "2.8.6",
33 | "tailwindcss": "^3.1.8",
34 | "typescript": "5.0.2",
35 | "undici": "5.21.0",
36 | "vite": "4.2.1",
37 | "vite-tsconfig-paths": "3.5.0"
38 | },
39 | "dependencies": {
40 | "@builder.io/qwik": "^0.100.0",
41 | "@builder.io/qwik-city": "^0.100.0",
42 | "@modular-forms/qwik": "^0.7.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { component$ } from '@builder.io/qwik';
2 | import { DocumentHead, Form } from '@builder.io/qwik-city';
3 |
4 | import { routeAction$, zod$, z } from '@builder.io/qwik-city';
5 |
6 | export const useAddUser = routeAction$(
7 | (user, { fail }) => {
8 | // `user` is typed { name: string }
9 | const userID = db.users.add(user);
10 | return {
11 | success: true,
12 | userID,
13 | };
14 | },
15 | zod$({
16 | name: z.string().nonempty(),
17 | })
18 | );
19 |
20 | export default component$(() => {
21 | const action = useAddUser();
22 | return (
23 | <>
24 |
25 | Qwik Server forms
26 |
36 |
37 | >
38 | );
39 | });
40 |
41 | // This is just to simulate a database
42 | const db = {
43 | users: {
44 | add: (user: { name: string }) => {
45 | console.log(user);
46 | const userID = Math.floor(Math.random() * 1000);
47 | return userID;
48 | },
49 | },
50 | };
51 |
52 | export const head: DocumentHead = {
53 | title: 'Qwik Forms',
54 | meta: [
55 | {
56 | name: 'description',
57 | content: 'Qwik Forms',
58 | },
59 | ],
60 | };
61 |
--------------------------------------------------------------------------------
/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 a 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 `npm run 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 | npm run 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 | npm run 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 | npm run build # or `yarn build`
65 | ```
66 |
--------------------------------------------------------------------------------
/src/components/icons/qwik.tsx:
--------------------------------------------------------------------------------
1 | export const QwikLogo = () => (
2 |
38 | );
39 |
--------------------------------------------------------------------------------
/src/routes/modular-forms/index.tsx:
--------------------------------------------------------------------------------
1 | import { component$, $ } from '@builder.io/qwik';
2 | import { DocumentHead } from '@builder.io/qwik-city';
3 |
4 | import { routeLoader$, z } from '@builder.io/qwik-city';
5 | import {
6 | type InitialValues,
7 | useForm,
8 | zodForm$,
9 | formAction$,
10 | SubmitHandler,
11 | } from '@modular-forms/qwik';
12 |
13 | const formSchema = z.object({
14 | email: z
15 | .string()
16 | .nonempty('please enter your email')
17 | .email('enter a valid email'),
18 | password: z
19 | .string()
20 | .min(1, 'please enter a password')
21 | .min(8, 'You password must have 8 characters or more.'),
22 | });
23 |
24 | type LoginForm = z.infer;
25 | type LoginResponse = { createdUserID: number };
26 |
27 | const getDataAfterXms = (data: any, ms: number): Promise =>
28 | new Promise((res) => setTimeout(() => res(data), ms));
29 |
30 | export const useFormLoader = routeLoader$>(() => ({
31 | email: '',
32 | password: '',
33 | }));
34 |
35 | export const useFormAction = formAction$(
36 | async ({ email, password }) => {
37 | // Runs on server
38 | const createdUserID = await getDataAfterXms(db.users.add({ email }), 2000);
39 | console.log(createdUserID);
40 | return {
41 | status: 'success',
42 | message: 'User added successfully',
43 | data: { createdUserID },
44 | };
45 | },
46 | zodForm$(formSchema)
47 | );
48 |
49 | export default component$(() => {
50 | const [loginForm, { Form, Field }] = useForm({
51 | loader: useFormLoader(),
52 | validate: zodForm$(formSchema),
53 | action: useFormAction(),
54 | });
55 |
56 | const handleSubmit: SubmitHandler = $((values, event) => {
57 | // Runs on client
58 | console.log(values);
59 | console.log(event);
60 | });
61 |
62 | return (
63 |
64 | Qwik Modular Forms
65 |
96 | {loginForm.response.status === 'success' && (
97 |
98 | Successfully created user in DB with ID:{' '}
99 | {loginForm.response.data?.createdUserID}
100 |
101 | )}
102 |
103 | );
104 | });
105 |
106 | // This is just to simulate a database
107 | const db = {
108 | users: {
109 | add: (user: { email: string }) => {
110 | console.log(user);
111 | const userID = Math.floor(Math.random() * 1000);
112 | return userID;
113 | },
114 | },
115 | };
116 |
117 | export const head: DocumentHead = {
118 | title: 'Qwik + Modular Forms',
119 | meta: [
120 | {
121 | name: 'description',
122 | content: 'Qwik + Modular Forms',
123 | },
124 | ],
125 | };
126 |
--------------------------------------------------------------------------------
/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 | /**
10 | * WHAT IS THIS FILE?
11 | *
12 | * Globally applied styles. No matter which components are in the page or matching route,
13 | * the styles in here will be applied to the Document, without any sort of CSS scoping.
14 | *
15 | */
16 |
17 | :root {
18 | --qwik-dark-blue: #006ce9;
19 | --qwik-light-blue: #18b6f6;
20 | --qwik-light-purple: #ac7ff4;
21 | --qwik-dark-purple: #713fc2;
22 | }
23 |
24 | body {
25 | background: linear-gradient(
26 | 90deg,
27 | rgba(24, 182, 246, 0.3) 0%,
28 | rgba(172, 127, 244, 0.3) 100%
29 | );
30 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
31 | sans-serif;
32 | font-weight: 200;
33 | padding: 10px 0px;
34 | }
35 |
36 | ::selection {
37 | background: rgb(8, 2, 61);
38 | color: white;
39 | }
40 |
41 | h1,
42 | h2,
43 | h3 {
44 | font-weight: 100;
45 | }
46 |
47 | h1 b,
48 | h2 b,
49 | h3 b {
50 | font-weight: 300;
51 | }
52 |
53 | h1 {
54 | color: white;
55 | font-size: 2rem;
56 | padding: 0;
57 | margin: 0;
58 | }
59 |
60 | h1.hero {
61 | margin: 60px 0;
62 | }
63 |
64 | h2 {
65 | color: var(--qwik-dark-purple);
66 | }
67 |
68 | input[type='text'],
69 | button {
70 | background-color: transparent;
71 | color: white;
72 | border: 1px solid white;
73 | border-radius: 12px;
74 | font-size: 1rem;
75 | padding: 10px 20px;
76 | }
77 |
78 | button {
79 | cursor: pointer;
80 | }
81 |
82 | button:hover {
83 | background-color: white;
84 | color: var(--qwik-dark-purple);
85 | }
86 |
87 | button.small {
88 | border-radius: 6px;
89 | font-size: 0.8rem;
90 | padding: 5px 10px;
91 | }
92 |
93 | button.gray {
94 | border-color: #aaa;
95 | color: #aaa;
96 | }
97 |
98 | a,
99 | a:active,
100 | a:visited,
101 | a:hover {
102 | color: black;
103 | font-weight: 400;
104 | text-decoration: none;
105 | }
106 |
107 | .section.bright {
108 | background: white;
109 | }
110 |
111 | .section.dark {
112 | background: rgba(50, 50, 50, 0.5);
113 | }
114 |
115 | code {
116 | background: rgba(230, 230, 230, 0.3);
117 | border-radius: 4px;
118 | padding: 2px 6px;
119 | }
120 |
121 | ul {
122 | margin: 0;
123 | padding-left: 20px;
124 | }
125 |
126 | .page {
127 | width: 90vw;
128 | max-width: 1200px;
129 | margin: 0 auto;
130 | background: linear-gradient(
131 | 90deg,
132 | rgba(24, 182, 246, 0.6) 0%,
133 | rgba(172, 127, 244, 0.6) 100%
134 | );
135 | overflow: hidden;
136 | border-radius: 6px;
137 | }
138 |
139 | .container {
140 | padding: 20px 10px;
141 | }
142 |
143 | .container.center {
144 | text-align: center;
145 | }
146 |
147 | .container.mh-300 {
148 | min-height: 300px;
149 | }
150 |
151 | .topics {
152 | display: grid;
153 | grid-template-columns: 1fr;
154 | }
155 |
156 | /* used icon pack: https://www.svgrepo.com/collection/phosphor-thin-icons */
157 | .icon:before {
158 | width: 18px;
159 | height: 18px;
160 | content: '';
161 | display: inline-block;
162 | margin-right: 5px;
163 | position: relative;
164 | top: 2px;
165 | }
166 |
167 | .icon-cli:before {
168 | background-image: url("data:image/svg+xml,%3Csvg fill='%23000000' 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");
169 | }
170 |
171 | .icon-apps:before {
172 | background-image: url("data:image/svg+xml,%3Csvg fill='%23000000' 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");
173 | }
174 |
175 | .icon-community:before {
176 | background-image: url("data:image/svg+xml,%3Csvg fill='%23000000' 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");
177 | }
178 |
179 | @media screen and (min-width: 768px) {
180 | body {
181 | padding: 60px 0px;
182 | }
183 |
184 | h1 {
185 | font-size: 3rem;
186 | }
187 |
188 | .page {
189 | width: 70vw;
190 | }
191 |
192 | .container {
193 | padding: 30px 70px;
194 | }
195 |
196 | .topics {
197 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
198 | gap: 20px;
199 | }
200 | }
201 |
202 | /**
203 | * WHAT IS THIS FILE?
204 | *
205 | * Globally applied styles. No matter which components are in the page or matching route,
206 | * the styles in here will be applied to the Document, without any sort of CSS scoping.
207 | *
208 | */
209 | html {
210 | line-height: 1.5;
211 | -webkit-text-size-adjust: 100%;
212 | -moz-tab-size: 4;
213 | -o-tab-size: 4;
214 | tab-size: 4;
215 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
216 | 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
217 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
218 | }
219 |
220 | body {
221 | padding: 0;
222 | line-height: inherit;
223 | }
224 |
--------------------------------------------------------------------------------