├── svelte-tutorial
├── src
│ ├── lib
│ │ ├── Counter.svelte
│ │ ├── Portal.svelte
│ │ ├── Summary.svelte
│ │ ├── Clocks.svelte
│ │ ├── Hero.svelte
│ │ ├── Calendar.svelte
│ │ ├── Form.svelte
│ │ └── layouts
│ │ │ └── Layout.svelte
│ ├── vite-env.d.ts
│ ├── main.js
│ ├── App.svelte
│ ├── assets
│ │ └── svelte.svg
│ ├── utils
│ │ └── index.js
│ ├── index.css
│ └── fanta.css
├── .vscode
│ └── extensions.json
├── vite.config.js
├── svelte.config.js
├── .gitignore
├── package.json
├── index.html
├── jsconfig.json
├── public
│ └── vite.svg
└── README.md
├── .gitattributes
├── vue-tutorial
├── .vscode
│ └── extensions.json
├── src
│ ├── components
│ │ ├── Summary.vue
│ │ ├── Portal.vue
│ │ ├── layouts
│ │ │ └── Layout.vue
│ │ ├── Clocks.vue
│ │ ├── HelloWorld.vue
│ │ ├── Hero.vue
│ │ ├── Calendar.vue
│ │ └── Form.vue
│ ├── main.js
│ ├── assets
│ │ └── vue.svg
│ ├── App.vue
│ ├── utils
│ │ └── index.js
│ ├── style.css
│ └── fanta.css
├── vite.config.js
├── .gitignore
├── package.json
├── index.html
├── public
│ └── vite.svg
└── package-lock.json
├── reactjs-tutorial
├── vite.config.js
├── src
│ ├── main.jsx
│ ├── components
│ │ ├── Portal.jsx
│ │ ├── Summary.jsx
│ │ ├── layouts
│ │ │ └── Layout.jsx
│ │ ├── Clocks.jsx
│ │ ├── Hero.jsx
│ │ ├── Calendar.jsx
│ │ └── Form.jsx
│ ├── App.jsx
│ ├── assets
│ │ └── react.svg
│ ├── utils
│ │ └── index.js
│ ├── index.css
│ └── fanta.css
├── .gitignore
├── index.html
├── package.json
├── README.md
├── eslint.config.js
└── public
│ └── vite.svg
└── README.md
/svelte-tutorial/src/lib/Counter.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/vue-tutorial/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/svelte-tutorial/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["svelte.svelte-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/vue-tutorial/src/components/Summary.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/vue-tutorial/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import './style.css'
3 | import './fanta.css'
4 | import App from './App.vue'
5 |
6 | createApp(App).mount('#app')
7 |
--------------------------------------------------------------------------------
/vue-tutorial/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [vue()],
7 | })
8 |
--------------------------------------------------------------------------------
/reactjs-tutorial/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/svelte-tutorial/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import { svelte } from '@sveltejs/vite-plugin-svelte'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [svelte()],
7 | })
8 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/main.js:
--------------------------------------------------------------------------------
1 | import { mount } from 'svelte'
2 | import './fanta.css'
3 | import './index.css'
4 | import App from './App.svelte'
5 |
6 | const app = mount(App, {
7 | target: document.getElementById('app'),
8 | })
9 |
10 | export default app
11 |
--------------------------------------------------------------------------------
/svelte-tutorial/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
2 |
3 | export default {
4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
5 | // for more information about preprocessors
6 | preprocess: vitePreprocess(),
7 | }
8 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import './fanta.css'
5 | import App from './App.jsx'
6 |
7 | createRoot(document.getElementById('root')).render(
8 |
9 |
10 | ,
11 | )
12 |
--------------------------------------------------------------------------------
/vue-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/svelte-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/reactjs-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | .env
27 |
--------------------------------------------------------------------------------
/svelte-tutorial/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-tutorial",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "devDependencies": {
12 | "@sveltejs/vite-plugin-svelte": "^5.0.3",
13 | "svelte": "^5.20.2",
14 | "vite": "^6.2.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/vue-tutorial/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-tutorial",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "vue": "^3.5.13"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-vue": "^5.2.1",
16 | "vite": "^6.2.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/Portal.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
{}}
10 | onclick={handleCloseModal}
11 | class="portal-underlay"
12 | >
13 |
14 | {@render form()}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/vue-tutorial/src/assets/vue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/Portal.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDom from 'react-dom'
3 |
4 | export default function Portal(props) {
5 | const { handleCloseModal, children } = props
6 |
7 | return ReactDom.createPortal(
8 |
9 |
10 |
11 | {children}
12 |
13 |
,
14 | document.getElementById('portal')
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/vue-tutorial/src/components/Portal.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/svelte-tutorial/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Unalive
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/App.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | {#snippet headache({
11 | data,
12 | birthDate,
13 | name,
14 | percentage,
15 | lifeExpectancy,
16 | handleToggleModal,
17 | resetData,
18 | })}
19 |
20 |
21 |
22 |
23 | {/snippet}
24 |
25 |
--------------------------------------------------------------------------------
/vue-tutorial/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Unalive
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/reactjs-tutorial/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Unalive
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/Summary.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 | You'll probably die in the year {finalYear} at the
14 | age of {lifeExpectancy} .
15 |
16 |
17 |
18 | Yesterday is history, tomorrow is a mystery, and today is a gift. That's
19 | why they call it the present.
20 |
21 | Master Oogway
22 |
23 |
--------------------------------------------------------------------------------
/reactjs-tutorial/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactjs-tutorial",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^19.0.0",
14 | "react-dom": "^19.0.0"
15 | },
16 | "devDependencies": {
17 | "@eslint/js": "^9.21.0",
18 | "@types/react": "^19.0.10",
19 | "@types/react-dom": "^19.0.4",
20 | "@vitejs/plugin-react": "^4.3.4",
21 | "eslint": "^9.21.0",
22 | "eslint-plugin-react-hooks": "^5.1.0",
23 | "eslint-plugin-react-refresh": "^0.4.19",
24 | "globals": "^15.15.0",
25 | "vite": "^6.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/Summary.jsx:
--------------------------------------------------------------------------------
1 | export default function Summary(props) {
2 | const { lifeExpectancy, birthDate } = props
3 | const finalYear = parseInt(birthDate.split('-')[0]) + lifeExpectancy
4 | return (
5 |
6 |
7 |
8 |
9 | You'll probably die in the year {finalYear} at the age of {lifeExpectancy} .
10 |
11 |
12 | Yesterday is history, tomorrow is a mystery, and today is a gift. That's why they call it the present.
13 | Master Oogway
14 |
15 | )
16 | }
--------------------------------------------------------------------------------
/vue-tutorial/src/components/layouts/Layout.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/Clocks.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | Time remaining in different units.
11 |
12 | {#each Object.keys(data) as clock, clockIndex}
13 |
14 |
17 |
18 |
{data[clock]}
19 |
{clock}
20 |
21 |
22 | {/each}
23 |
24 |
25 |
--------------------------------------------------------------------------------
/reactjs-tutorial/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend using TypeScript and enable type-aware lint rules. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
13 |
--------------------------------------------------------------------------------
/reactjs-tutorial/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import reactHooks from 'eslint-plugin-react-hooks'
4 | import reactRefresh from 'eslint-plugin-react-refresh'
5 |
6 | export default [
7 | { ignores: ['dist'] },
8 | {
9 | files: ['**/*.{js,jsx}'],
10 | languageOptions: {
11 | ecmaVersion: 2020,
12 | globals: globals.browser,
13 | parserOptions: {
14 | ecmaVersion: 'latest',
15 | ecmaFeatures: { jsx: true },
16 | sourceType: 'module',
17 | },
18 | },
19 | plugins: {
20 | 'react-hooks': reactHooks,
21 | 'react-refresh': reactRefresh,
22 | },
23 | rules: {
24 | ...js.configs.recommended.rules,
25 | ...reactHooks.configs.recommended.rules,
26 | 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
27 | 'react-refresh/only-export-components': [
28 | 'warn',
29 | { allowConstantExport: true },
30 | ],
31 | },
32 | },
33 | ]
34 |
--------------------------------------------------------------------------------
/vue-tutorial/src/components/Clocks.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 | Time remaining in different units.
14 |
15 |
16 |
21 |
22 |
{{ data.value[clock] }}
23 |
{{ clock }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/vue-tutorial/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | {{ msg }}
13 |
14 |
15 |
count is {{ count }}
16 |
17 | Edit
18 | components/HelloWorld.vue to test HMR
19 |
20 |
21 |
22 |
23 | Check out
24 | create-vue , the official Vue + Vite starter
27 |
28 |
29 | Learn more about IDE Support for Vue in the
30 | Vue Docs Scaling up Guide .
35 |
36 | Click on the Vite and Vue logos to learn more
37 |
38 |
39 |
44 |
--------------------------------------------------------------------------------
/svelte-tutorial/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "bundler",
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | /**
7 | * svelte-preprocess cannot figure out whether you have
8 | * a value or a type, so tell TypeScript to enforce using
9 | * `import type` instead of `import` for Types.
10 | */
11 | "verbatimModuleSyntax": true,
12 | "isolatedModules": true,
13 | "resolveJsonModule": true,
14 | /**
15 | * To have warnings / errors of the Svelte compiler at the
16 | * correct position, enable source maps by default.
17 | */
18 | "sourceMap": true,
19 | "esModuleInterop": true,
20 | "skipLibCheck": true,
21 | /**
22 | * Typecheck JS in `.svelte` and `.js` files by default.
23 | * Disable this if you'd like to use dynamic types.
24 | */
25 | "checkJs": true
26 | },
27 | /**
28 | * Use global.d.ts instead of compilerOptions.types
29 | * to avoid limiting type declarations.
30 | */
31 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
32 | }
33 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/layouts/Layout.jsx:
--------------------------------------------------------------------------------
1 | export default function Layout(props) {
2 | const { children } = props
3 |
4 | const header = (
5 |
10 | )
11 |
12 | const footer = (
13 |
21 | )
22 |
23 | return (
24 | <>
25 | {header}
26 | < main >
27 | {children}
28 |
29 | {footer}
30 | >
31 | )
32 | }
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/Clocks.jsx:
--------------------------------------------------------------------------------
1 | import { getTimePercentage } from "../utils"
2 |
3 | function Clock(props) {
4 | const { percent, title, data } = props
5 | return (
6 |
7 |
10 |
11 |
{data}
12 |
{title}
13 |
14 |
15 | )
16 | }
17 |
18 |
19 | export default function Clocks(props) {
20 | const { data } = props
21 | const snapshot = getTimePercentage()
22 | return (
23 |
24 | Time remaining in different units.
25 |
26 | {Object.keys(data).map((clock, clockIndex) => {
27 | return (
28 |
29 | )
30 | })}
31 |
32 |
33 | )
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/Hero.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | {name}, you have {data["weeks"]} weeks left. Make them count 🫡
9 |
10 |
11 | Not {name}?
12 | {
14 | navigator.clipboard.writeText("https://www.smoljames.com");
15 | alert("Copied :)");
16 | }}
17 | class="link-button">Copy link
19 | Reset data
20 |
21 |
22 |
23 |
24 |
25 |
Birth
26 |
27 |
{percentage}
28 |
29 |
30 |
Death
31 |
32 |
33 |
34 |
35 |
36 |
38 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/Calendar.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 | Each square of dots represents 52 weeks / 1 year of your life.
14 |
15 |
16 | {#each yearsArr as year, yearIndex}
17 |
18 | {#each weeksArr.map((val, valIndex) => {
19 | const currWeek = yearIndex * 52 + valIndex;
20 | const dotStyle = currWeek == finalWeek - 1 ? " death" : currWeek < weekNum ? " solid" : currWeek == weekNum ? " pulse" : " ";
21 | return { week: val, currWeek, dotStyle };
22 | }) as week, weekIndex}
23 |
24 | {/each}
25 |
26 | {/each}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/Hero.jsx:
--------------------------------------------------------------------------------
1 | export default function Hero(props) {
2 | const { name, resetData, data, percentage, handleToggleModal } = props
3 |
4 | return (
5 |
6 |
7 | {name}, you have {data.weeks} weeks left. Make them count 🫡
8 |
9 |
10 | Not {name}?
11 | {
12 | navigator.clipboard.writeText('https://www.smoljames.com')
13 | alert('Copied :)')
14 | }} className="link-button">Copy link
15 | Reset data
16 |
17 |
18 |
19 |
20 |
21 |
Birth
22 |
23 |
{percentage}
24 |
25 |
26 |
Death
27 |
28 |
29 |
30 |
31 | )
32 | }
--------------------------------------------------------------------------------
/svelte-tutorial/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vue-tutorial/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/reactjs-tutorial/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vue-tutorial/src/components/Hero.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 | {{ name }}, you have {{ data['weeks'] }} weeks left. Make them count 🫡
20 |
21 |
22 | Not {{ name }}?
23 | Copy link
24 | Reset data
25 |
26 |
27 |
28 |
29 |
30 |
Birth
31 |
32 |
{{ percentage }}
33 |
34 |
35 |
Death
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/Calendar.jsx:
--------------------------------------------------------------------------------
1 | function DozenDisplay(props) {
2 | const { weeksArr, yearIndex, weekNum, finalWeek } = props
3 | return (
4 |
5 | {weeksArr.map((week, weekIndex) => {
6 | const currWeek = yearIndex * 52 + weekIndex
7 | const dotStyle = currWeek == finalWeek - 1 ?
8 | ' death' :
9 | currWeek < weekNum ?
10 | ' solid' :
11 | currWeek == weekNum ?
12 | ' pulse' : ''
13 |
14 | return (
15 |
16 | )
17 | })}
18 |
19 | )
20 | }
21 |
22 | export default function Calendar(props) {
23 | const { lifeExpectancy, data } = props
24 | const yearsArr = [...Array(lifeExpectancy).keys()]
25 | const weeksArr = [...Array(52).keys()]
26 |
27 | const weekNum = lifeExpectancy * 52 - parseInt(data['weeks'])
28 | const finalWeek = lifeExpectancy * 52
29 |
30 | return (
31 |
32 | Each square of dots represents 52 weeks / 1 year of your life.
33 |
34 | {yearsArr.map((year, yearIndex) => {
35 | return (
36 |
37 | )
38 | })}
39 |
40 |
41 | )
42 | }
--------------------------------------------------------------------------------
/vue-tutorial/src/components/Calendar.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 | Each square of dots represents 52 weeks / 1 year of your life.
31 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/assets/svelte.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/Form.svelte:
--------------------------------------------------------------------------------
1 |
31 |
32 |
83 |
--------------------------------------------------------------------------------
/vue-tutorial/src/components/Form.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react"
2 | import Calendar from "./components/Calendar"
3 | import Clocks from "./components/Clocks"
4 | import Form from "./components/Form"
5 | import Hero from "./components/Hero"
6 | import Layout from "./components/layouts/Layout"
7 | import Portal from "./components/Portal"
8 | import Summary from "./components/Summary"
9 | import { calculateTimeLeft, getLifePercentageLived } from "./utils"
10 |
11 |
12 | function App() {
13 | const [name, setName] = useState('James')
14 | const [birthDate, setBirthDate] = useState('1995-06-15')
15 | const [lifeExpectancy, setLifeExpectancy] = useState(80)
16 | const [showModal, setShowModal] = useState(false)
17 | const [data, setData] = useState(calculateTimeLeft(birthDate, lifeExpectancy))
18 |
19 | function handleToggleModal() {
20 | setShowModal(curr => !curr)
21 | }
22 |
23 | function resetData() {
24 | setName('James')
25 | setBirthDate('1995-06-15')
26 | setLifeExpectancy(80)
27 | localStorage.clear()
28 | }
29 |
30 | function handleUpdateData(n, b, e) {
31 | if (!n || !b || !e) { return }
32 |
33 | // save data to local storage so that i can read it in later
34 | localStorage.setItem('formData', JSON.stringify({ name: n, birthDate: b, lifeExpectancy: e }))
35 |
36 | setName(n)
37 | setBirthDate(b)
38 | setLifeExpectancy(parseInt(e))
39 | handleToggleModal()
40 | }
41 |
42 | const percentage = getLifePercentageLived(birthDate, lifeExpectancy)
43 |
44 |
45 | useEffect(() => {
46 | if (!localStorage) { return }
47 | if (localStorage.getItem('formData')) {
48 | const { name: n, birthDate: b, lifeExpectancy: e } = JSON.parse(localStorage.getItem('formData'))
49 | setName(n)
50 | setBirthDate(b)
51 | setLifeExpectancy(parseInt(e))
52 | }
53 | }, [])
54 |
55 | useEffect(() => {
56 | const interval = setInterval(() => {
57 | setData(calculateTimeLeft(birthDate, lifeExpectancy))
58 | }, 1000)
59 | return () => { clearInterval(interval) }
60 | }, [birthDate, lifeExpectancy])
61 |
62 | return (
63 |
64 | {showModal && (
65 |
66 |
67 |
68 | )}
69 |
70 |
71 |
72 |
73 |
74 | )
75 | }
76 |
77 | export default App
78 |
--------------------------------------------------------------------------------
/vue-tutorial/src/App.vue:
--------------------------------------------------------------------------------
1 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/svelte-tutorial/README.md:
--------------------------------------------------------------------------------
1 | # Svelte + Vite
2 |
3 | This template should help get you started developing with Svelte in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
8 |
9 | ## Need an official Svelte framework?
10 |
11 | Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
12 |
13 | ## Technical considerations
14 |
15 | **Why use this over SvelteKit?**
16 |
17 | - It brings its own routing solution which might not be preferable for some users.
18 | - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
19 |
20 | This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
21 |
22 | Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
23 |
24 | **Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
25 |
26 | Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
27 |
28 | **Why include `.vscode/extensions.json`?**
29 |
30 | Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
31 |
32 | **Why enable `checkJs` in the JS template?**
33 |
34 | It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration.
35 |
36 | **Why is HMR not preserving my local component state?**
37 |
38 | HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/sveltejs/svelte-hmr/tree/master/packages/svelte-hmr#preservation-of-local-state).
39 |
40 | If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
41 |
42 | ```js
43 | // store.js
44 | // An extremely simple external store
45 | import { writable } from 'svelte/store'
46 | export default writable(0)
47 | ```
48 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/components/Form.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 |
3 | export default function Form(props) {
4 | const { handleCloseModal, handleUpdateData } = props
5 | const [name, setName] = useState('James')
6 | const [lifeExpectancy, setLifeExpectancy] = useState(80)
7 |
8 | const [month, setMonth] = useState(1) // stored as index from 1-12
9 | const [day, setDay] = useState(1)
10 | const [year, setYear] = useState(new Date().getFullYear())
11 |
12 | const days = Array.from({ length: 31 }, (_, i) => i + 1)
13 | const months = [
14 | "January", "February", "March", "April", "May", "June",
15 | "July", "August", "September", "October", "November", "December"
16 | ]
17 | const years = Array.from({ length: 100 }, (_, i) => new Date().getFullYear() - i)
18 |
19 | return (
20 |
80 | )
81 | }
82 |
--------------------------------------------------------------------------------
/svelte-tutorial/src/lib/layouts/Layout.svelte:
--------------------------------------------------------------------------------
1 |
82 |
83 | {#if showModal}
84 |
85 | {#snippet form()}
86 |
87 | {/snippet}
88 |
89 | {/if}
90 |
91 |
94 |
95 |
96 |
97 | {@render headache({
98 | data,
99 | birthDate,
100 | name,
101 | percentage,
102 | lifeExpectancy,
103 | handleToggleModal,
104 | resetData,
105 | })}
106 |
107 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/reactjs-tutorial/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vue-tutorial/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export function calculateTimeLeft(birthDate, lifeExpectancy) {
2 | const now = new Date()
3 | const birth = new Date(birthDate)
4 | const expectedDeath = new Date(birth.setFullYear(birth.getFullYear() + lifeExpectancy))
5 |
6 | if (now >= expectedDeath) {
7 | console.log("You have surpassed the life expectancy!")
8 | return ({
9 | years: '-',
10 | months: '-',
11 | weeks: '-',
12 | days: '-',
13 | hours: '-',
14 | minutes: '-',
15 | seconds: '-'
16 | })
17 | }
18 |
19 | const millisecondsLeft = expectedDeath - now
20 | const secondsLeft = Math.floor(millisecondsLeft / 1000)
21 | const minutesLeft = Math.floor(secondsLeft / 60)
22 | const hoursLeft = Math.floor(minutesLeft / 60)
23 | const daysLeft = Math.floor(hoursLeft / 24)
24 | const weeksLeft = Math.floor(daysLeft / 7)
25 |
26 | // Approximate months and years
27 | const monthsLeft = Math.floor(daysLeft / 30.44)
28 | const yearsLeft = Math.floor(monthsLeft / 12)
29 |
30 | return {
31 | years: yearsLeft,
32 | months: monthsLeft,
33 | weeks: weeksLeft,
34 | days: daysLeft,
35 | hours: hoursLeft,
36 | minutes: minutesLeft,
37 | seconds: secondsLeft
38 | }
39 | }
40 |
41 | export function getTimePercentage() {
42 | const now = new Date()
43 |
44 | // Year
45 | const startOfYear = new Date(now.getFullYear(), 0, 1)
46 | const endOfYear = new Date(now.getFullYear() + 1, 0, 1)
47 | const yearPercentage = ((now - startOfYear) / (endOfYear - startOfYear)) * 100
48 |
49 | // Month
50 | const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
51 | const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1)
52 | const monthPercentage = ((now - startOfMonth) / (endOfMonth - startOfMonth)) * 100
53 |
54 | // Week (ISO week: Monday = 0, Sunday = 6)
55 | const startOfWeek = new Date(now)
56 | startOfWeek.setDate(now.getDate() - now.getDay())
57 | startOfWeek.setHours(0, 0, 0, 0)
58 | const endOfWeek = new Date(startOfWeek)
59 | endOfWeek.setDate(startOfWeek.getDate() + 7)
60 | const weekPercentage = ((now - startOfWeek) / (endOfWeek - startOfWeek)) * 100
61 |
62 | // Day
63 | const startOfDay = new Date(now)
64 | startOfDay.setHours(0, 0, 0, 0)
65 | const endOfDay = new Date(startOfDay)
66 | endOfDay.setDate(startOfDay.getDate() + 1)
67 | const dayPercentage = ((now - startOfDay) / (endOfDay - startOfDay)) * 100
68 |
69 | // Hour
70 | const startOfHour = new Date(now)
71 | startOfHour.setMinutes(0, 0, 0)
72 | const endOfHour = new Date(startOfHour)
73 | endOfHour.setHours(startOfHour.getHours() + 1)
74 | const hourPercentage = ((now - startOfHour) / (endOfHour - startOfHour)) * 100
75 |
76 | // Minute
77 | const startOfMinute = new Date(now)
78 | startOfMinute.setSeconds(0, 0)
79 | const endOfMinute = new Date(startOfMinute)
80 | endOfMinute.setMinutes(startOfMinute.getMinutes() + 1)
81 | const minutePercentage = ((now - startOfMinute) / (endOfMinute - startOfMinute)) * 100
82 |
83 | return {
84 | years: yearPercentage.toFixed(1) * 360 / 100,
85 | months: monthPercentage.toFixed(1) * 360 / 100,
86 | weeks: weekPercentage.toFixed(1) * 360 / 100,
87 | days: dayPercentage.toFixed(1) * 360 / 100,
88 | hours: hourPercentage.toFixed(1) * 360 / 100,
89 | minutes: minutePercentage.toFixed(1) * 360 / 100
90 | }
91 | }
92 |
93 |
94 | export function getLifePercentageLived(birthDate, lifeExpectancy) {
95 | const birth = new Date(birthDate)
96 | const now = new Date()
97 | const expectedDeath = new Date(birth)
98 | expectedDeath.setFullYear(birth.getFullYear() + lifeExpectancy)
99 |
100 | const lifeLived = now - birth
101 | const totalLife = expectedDeath - birth
102 | const lifePercentage = (lifeLived / totalLife) * 100
103 | return lifePercentage.toFixed(1) + "%"
104 | }
--------------------------------------------------------------------------------
/svelte-tutorial/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export function calculateTimeLeft(birthDate, lifeExpectancy) {
2 | const now = new Date()
3 | const birth = new Date(birthDate)
4 | const expectedDeath = new Date(birth.setFullYear(birth.getFullYear() + lifeExpectancy))
5 |
6 | if (now >= expectedDeath) {
7 | console.log("You have surpassed the life expectancy!")
8 | return ({
9 | years: '-',
10 | months: '-',
11 | weeks: '-',
12 | days: '-',
13 | hours: '-',
14 | minutes: '-',
15 | seconds: '-'
16 | })
17 | }
18 |
19 | const millisecondsLeft = expectedDeath - now
20 | const secondsLeft = Math.floor(millisecondsLeft / 1000)
21 | const minutesLeft = Math.floor(secondsLeft / 60)
22 | const hoursLeft = Math.floor(minutesLeft / 60)
23 | const daysLeft = Math.floor(hoursLeft / 24)
24 | const weeksLeft = Math.floor(daysLeft / 7)
25 |
26 | // Approximate months and years
27 | const monthsLeft = Math.floor(daysLeft / 30.44)
28 | const yearsLeft = Math.floor(monthsLeft / 12)
29 |
30 | return {
31 | years: yearsLeft,
32 | months: monthsLeft,
33 | weeks: weeksLeft,
34 | days: daysLeft,
35 | hours: hoursLeft,
36 | minutes: minutesLeft,
37 | seconds: secondsLeft
38 | }
39 | }
40 |
41 | export function getTimePercentage() {
42 | const now = new Date()
43 |
44 | // Year
45 | const startOfYear = new Date(now.getFullYear(), 0, 1)
46 | const endOfYear = new Date(now.getFullYear() + 1, 0, 1)
47 | const yearPercentage = ((now - startOfYear) / (endOfYear - startOfYear)) * 100
48 |
49 | // Month
50 | const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
51 | const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1)
52 | const monthPercentage = ((now - startOfMonth) / (endOfMonth - startOfMonth)) * 100
53 |
54 | // Week (ISO week: Monday = 0, Sunday = 6)
55 | const startOfWeek = new Date(now)
56 | startOfWeek.setDate(now.getDate() - now.getDay())
57 | startOfWeek.setHours(0, 0, 0, 0)
58 | const endOfWeek = new Date(startOfWeek)
59 | endOfWeek.setDate(startOfWeek.getDate() + 7)
60 | const weekPercentage = ((now - startOfWeek) / (endOfWeek - startOfWeek)) * 100
61 |
62 | // Day
63 | const startOfDay = new Date(now)
64 | startOfDay.setHours(0, 0, 0, 0)
65 | const endOfDay = new Date(startOfDay)
66 | endOfDay.setDate(startOfDay.getDate() + 1)
67 | const dayPercentage = ((now - startOfDay) / (endOfDay - startOfDay)) * 100
68 |
69 | // Hour
70 | const startOfHour = new Date(now)
71 | startOfHour.setMinutes(0, 0, 0)
72 | const endOfHour = new Date(startOfHour)
73 | endOfHour.setHours(startOfHour.getHours() + 1)
74 | const hourPercentage = ((now - startOfHour) / (endOfHour - startOfHour)) * 100
75 |
76 | // Minute
77 | const startOfMinute = new Date(now)
78 | startOfMinute.setSeconds(0, 0)
79 | const endOfMinute = new Date(startOfMinute)
80 | endOfMinute.setMinutes(startOfMinute.getMinutes() + 1)
81 | const minutePercentage = ((now - startOfMinute) / (endOfMinute - startOfMinute)) * 100
82 |
83 | return {
84 | years: yearPercentage.toFixed(1) * 360 / 100,
85 | months: monthPercentage.toFixed(1) * 360 / 100,
86 | weeks: weekPercentage.toFixed(1) * 360 / 100,
87 | days: dayPercentage.toFixed(1) * 360 / 100,
88 | hours: hourPercentage.toFixed(1) * 360 / 100,
89 | minutes: minutePercentage.toFixed(1) * 360 / 100
90 | }
91 | }
92 |
93 |
94 | export function getLifePercentageLived(birthDate, lifeExpectancy) {
95 | const birth = new Date(birthDate)
96 | const now = new Date()
97 | const expectedDeath = new Date(birth)
98 | expectedDeath.setFullYear(birth.getFullYear() + lifeExpectancy)
99 |
100 | const lifeLived = now - birth
101 | const totalLife = expectedDeath - birth
102 | const lifePercentage = (lifeLived / totalLife) * 100
103 | return lifePercentage.toFixed(1) + "%"
104 | }
--------------------------------------------------------------------------------
/reactjs-tutorial/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export function calculateTimeLeft(birthDate, lifeExpectancy) {
2 | const now = new Date()
3 | const birth = new Date(birthDate)
4 | const expectedDeath = new Date(birth.setFullYear(birth.getFullYear() + parseInt(lifeExpectancy)))
5 |
6 | if (now >= expectedDeath) {
7 | console.log("You have surpassed the life expectancy!")
8 | return ({
9 | years: '-',
10 | months: '-',
11 | weeks: '-',
12 | days: '-',
13 | hours: '-',
14 | minutes: '-',
15 | seconds: '-'
16 | })
17 | }
18 |
19 | const millisecondsLeft = expectedDeath - now
20 | const secondsLeft = Math.floor(millisecondsLeft / 1000)
21 | const minutesLeft = Math.floor(secondsLeft / 60)
22 | const hoursLeft = Math.floor(minutesLeft / 60)
23 | const daysLeft = Math.floor(hoursLeft / 24)
24 | const weeksLeft = Math.floor(daysLeft / 7)
25 |
26 | // Approximate months and years
27 | const monthsLeft = Math.floor(daysLeft / 30.44)
28 | const yearsLeft = Math.floor(monthsLeft / 12)
29 |
30 | return {
31 | years: yearsLeft,
32 | months: monthsLeft,
33 | weeks: weeksLeft,
34 | days: daysLeft,
35 | hours: hoursLeft,
36 | minutes: minutesLeft,
37 | seconds: secondsLeft
38 | }
39 | }
40 |
41 | export function getTimePercentage() {
42 | const now = new Date()
43 |
44 | // Year
45 | const startOfYear = new Date(now.getFullYear(), 0, 1)
46 | const endOfYear = new Date(now.getFullYear() + 1, 0, 1)
47 | const yearPercentage = ((now - startOfYear) / (endOfYear - startOfYear)) * 100
48 |
49 | // Month
50 | const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
51 | const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1)
52 | const monthPercentage = ((now - startOfMonth) / (endOfMonth - startOfMonth)) * 100
53 |
54 | // Week (ISO week: Monday = 0, Sunday = 6)
55 | const startOfWeek = new Date(now)
56 | startOfWeek.setDate(now.getDate() - now.getDay())
57 | startOfWeek.setHours(0, 0, 0, 0)
58 | const endOfWeek = new Date(startOfWeek)
59 | endOfWeek.setDate(startOfWeek.getDate() + 7)
60 | const weekPercentage = ((now - startOfWeek) / (endOfWeek - startOfWeek)) * 100
61 |
62 | // Day
63 | const startOfDay = new Date(now)
64 | startOfDay.setHours(0, 0, 0, 0)
65 | const endOfDay = new Date(startOfDay)
66 | endOfDay.setDate(startOfDay.getDate() + 1)
67 | const dayPercentage = ((now - startOfDay) / (endOfDay - startOfDay)) * 100
68 |
69 | // Hour
70 | const startOfHour = new Date(now)
71 | startOfHour.setMinutes(0, 0, 0)
72 | const endOfHour = new Date(startOfHour)
73 | endOfHour.setHours(startOfHour.getHours() + 1)
74 | const hourPercentage = ((now - startOfHour) / (endOfHour - startOfHour)) * 100
75 |
76 | // Minute
77 | const startOfMinute = new Date(now)
78 | startOfMinute.setSeconds(0, 0)
79 | const endOfMinute = new Date(startOfMinute)
80 | endOfMinute.setMinutes(startOfMinute.getMinutes() + 1)
81 | const minutePercentage = ((now - startOfMinute) / (endOfMinute - startOfMinute)) * 100
82 |
83 | return {
84 | years: yearPercentage.toFixed(1) * 360 / 100,
85 | months: monthPercentage.toFixed(1) * 360 / 100,
86 | weeks: weekPercentage.toFixed(1) * 360 / 100,
87 | days: dayPercentage.toFixed(1) * 360 / 100,
88 | hours: hourPercentage.toFixed(1) * 360 / 100,
89 | minutes: minutePercentage.toFixed(1) * 360 / 100
90 | }
91 | }
92 |
93 |
94 | export function getLifePercentageLived(birthDate, lifeExpectancy) {
95 | const birth = new Date(birthDate)
96 | const now = new Date()
97 | const expectedDeath = new Date(birth)
98 |
99 | expectedDeath.setFullYear(birth.getFullYear() + parseInt(lifeExpectancy))
100 |
101 | const lifeLived = now - birth
102 | const totalLife = expectedDeath - birth
103 | const lifePercentage = (lifeLived / totalLife) * 100
104 | return lifePercentage.toFixed(1) + "%"
105 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frontend Full Course 2025 | Learn React.js, Svelte & Vue.js in 8 Hours 🚀
2 |
3 | Master modern frontend development in 2025 by learning **React.js**, **Svelte**, and **Vue.js** — the most popular JavaScript frameworks today! Build the modern web application **three times**, starting from scratch in each framework, and gain job-ready frontend skills that will set you apart as a developer.
4 |
5 | This is the ultimate beginner-friendly course to confidently learn the core concepts of component-based frontend frameworks, compare them side by side, and pick your favorite!
6 |
7 | [➡️ **Watch the full course for free on YouTube (7.5 hours of hands-on tutorials)!**](https://www.youtu.be/rvwwHx2pJuw)
8 |
9 | ---
10 |
11 | ### **The Course Includes**
12 | 🎥 7.5 hours of step-by-step frontend development tutorials.
13 | ⭐️ Beginner-friendly explanations of React, Svelte, and Vue fundamentals.
14 | 🧩 One real-world application built in each framework — from scratch.
15 | 🎨 Pre-styled apps so you can focus 100% on JavaScript frameworks, not CSS.
16 | 💡 Access on mobile, tablet, desktop, and smart TV.
17 |
18 | ---
19 |
20 | ### **Who This Course Is For**
21 | ✅ Beginners ready to level up their frontend skills.
22 | ✅ Developers curious about the differences between React, Svelte, and Vue.
23 | ✅ Anyone who wants to confidently choose the best frontend framework for their projects.
24 | ✅ Self-taught coders following the [smoljames.com/roadmap](https://www.smoljames.com/roadmap).
25 |
26 | ---
27 |
28 | ### **Requirements**
29 | You’ll just need some basic understanding of:
30 | - **HTML**
31 | - **CSS**
32 | - **JavaScript**
33 |
34 | If you need to learn these first, head over to my free resources at: [smoljames.com/roadmap](https://www.smoljames.com/roadmap) 🌟
35 |
36 | Any computer (Windows, macOS, or Linux) is perfect — we’ll set everything up together during the course.
37 |
38 | ---
39 |
40 | ### **Course Description**
41 |
42 | #### **Why is this the right frontend development course for you?**
43 | This course is designed to teach you modern frontend development by building a full application three times over — once with **React.js**, once with **Svelte**, and once with **Vue.js**.
44 |
45 | By the end of the course, you’ll deeply understand:
46 | - How these frameworks think.
47 | - How to build dynamic web apps.
48 | - How to manage state, handle events, and navigate routes.
49 | - And how to compare frameworks confidently!
50 |
51 | These three are not just popular — they're **foundational**. Once you’ve learned React, Svelte, and Vue, you'll have the skills to easily pick up any other frontend framework or library.
52 |
53 | Plus, our app is pre-styled to keep the focus purely on **JavaScript frameworks and development concepts.**
54 |
55 | ---
56 |
57 | ### **What You’ll Build**
58 | - **React.js:** Build a fully functional app from scratch, understanding components, hooks, and state management.
59 | - **Svelte:** Build the same app with Svelte’s reactive, compiler-first approach for lightning-fast development.
60 | - **Vue.js:** Recreate the app in Vue with its intuitive syntax and flexible features.
61 |
62 | Repetition across frameworks locks in your understanding and helps you see the differences clearly.
63 |
64 | ---
65 |
66 | ### **What You’ll Learn**
67 | - Modern frontend development workflows.
68 | - Building apps with reusable components.
69 | - State management across frameworks.
70 | - Handling events and user interactions.
71 | - Client-side routing for single-page applications (SPAs).
72 | - Understanding reactivity and lifecycle hooks.
73 | - Key similarities and differences between frameworks.
74 | - How to choose the right framework for your projects.
75 |
76 | ---
77 |
78 | ### **About Your Instructor**
79 |
80 | Hi, I’m James! 👋
81 | I’ve taught over 500,000 people how to code and start their development careers. As a self-taught programmer, I know how overwhelming it can be to choose the right tools. This is why I built this course: to give you clarity, confidence, and practical skills that employers and clients are looking for.
82 |
83 | With over 10 years of teaching experience, I specialize in making complex topics easy to understand — and most importantly, fun.
84 |
85 | Let’s get started!
86 |
87 | Learn more at [smoljames.com](https://www.smoljames.com).
88 | Connect with me anytime on Discord or LinkedIn for support!
89 |
90 | ---
91 |
92 | ### **Tags**
93 | frontend course, react.js tutorial, svelte tutorial, vue.js tutorial, javascript frameworks, frontend development 2025, learn react svelte vue, youtube coding course, smoljames, frontend frameworks for beginners, frontend roadmap, web development full course
94 |
95 | ---
96 |
97 | 🚀 **Start your frontend journey today and master React, Svelte, and Vue — all in one course!**
98 |
--------------------------------------------------------------------------------
/vue-tutorial/src/style.css:
--------------------------------------------------------------------------------
1 | #app {
2 | max-width: 800px;
3 | width: 100%;
4 | margin: 0 auto;
5 | display: flex;
6 | flex-direction: column;
7 | gap: 2rem;
8 | min-height: 100vh;
9 | }
10 |
11 | header,
12 | main,
13 | footer {
14 | padding: 1rem;
15 | }
16 |
17 | main {
18 | flex: 1;
19 | gap: 4rem;
20 | }
21 |
22 | section,
23 | main {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | section {
29 | gap: 1.5rem;
30 | }
31 |
32 | .btns-container {
33 | display: flex;
34 | align-items: center;
35 | gap: 0.5rem;
36 | padding: 0.5rem 0;
37 | }
38 |
39 | .btns-container button {
40 | flex: 1;
41 | white-space: nowrap;
42 | }
43 |
44 | .progress-bar {
45 | border-radius: 4rem;
46 | border: 2px solid var(--color-link);
47 | }
48 |
49 | .progress-bar>div {
50 | padding: 0.8rem 1.4rem;
51 | }
52 |
53 | .progress-bar>div,
54 | .progress-bar {
55 | display: flex;
56 | align-items: center;
57 | justify-content: space-between;
58 | gap: 1rem;
59 | overflow: hidden;
60 | }
61 |
62 | .progress-bar>div:first-of-type {
63 | min-width: 20%;
64 | background: var(--color-link);
65 | color: var(--background-primary);
66 | }
67 |
68 | .progress-bar div div {
69 | display: flex;
70 | align-items: center;
71 | gap: 1rem;
72 | }
73 |
74 | .progress-bar .bar-label {
75 | display: none;
76 | }
77 |
78 | .clocks-grid {
79 | display: grid;
80 | grid-template-columns: repeat(2, minmax(0, 1fr));
81 | gap: 1rem;
82 | }
83 |
84 |
85 | .clock-card {
86 | display: flex;
87 | flex-direction: column;
88 | align-items: center;
89 | justify-content: center;
90 | gap: 0.5rem;
91 | }
92 |
93 | .clock-card>div:last-of-type {
94 | display: flex;
95 | flex-direction: column;
96 | align-items: center;
97 | }
98 |
99 | .clock-card>div:last-of-type p {
100 | text-transform: lowercase;
101 | opacity: 0.7;
102 | }
103 |
104 | .circle {
105 | height: 4rem;
106 | aspect-ratio: 1/1;
107 | margin: 0.5rem 0;
108 | border-radius: 100%;
109 | border: 3px solid var(--border-primary);
110 | position: relative;
111 | }
112 |
113 | .circle .ticker {
114 | height: 50%;
115 | width: 3px;
116 | background: var(--border-secondary);
117 | border-radius: 1rem;
118 | position: absolute;
119 | top: 0;
120 | left: 50%;
121 | transform: translateX(-50%);
122 | transform-origin: bottom;
123 | }
124 |
125 | .years,
126 | .weeks,
127 | .days,
128 | .hours,
129 | .minutes,
130 | .seconds {
131 | animation: spin linear infinite;
132 | }
133 |
134 |
135 | .years {
136 | animation-duration: 31556952s;
137 | /* 1 year */
138 | }
139 |
140 | .months {
141 | animation-duration: 2629746s;
142 | /* 1 month */
143 | }
144 |
145 | .weeks {
146 | animation-duration: 604800s;
147 | /* 1 week */
148 | }
149 |
150 | .days {
151 | animation-duration: 86400s;
152 | /* 1 day */
153 | }
154 |
155 | .hours {
156 | animation-duration: 3600s;
157 | /* 1 hour */
158 | }
159 |
160 | .minutes {
161 | animation-duration: 60s;
162 | /* 1 minute */
163 | }
164 |
165 | .seconds {
166 | animation-duration: 1s;
167 | /* 1 minute */
168 | }
169 |
170 | @keyframes spin {
171 | from {
172 | transform: rotate(0deg);
173 | }
174 |
175 | to {
176 | transform: rotate(360deg);
177 | }
178 | }
179 |
180 | .dozen-grid {
181 | display: grid;
182 | grid-template-columns: repeat(12, minmax(0, 1fr));
183 | gap: 0.5rem;
184 | }
185 |
186 | .year-grid {
187 | display: grid;
188 | grid-template-columns: repeat(4, minmax(0, 1fr));
189 | gap: 0.15rem 0;
190 | }
191 |
192 | .dot {
193 | height: 0.2rem;
194 | aspect-ratio: 1/1;
195 | border-radius: 0.2rem;
196 | border: 0.5px solid var(--color-link);
197 | }
198 |
199 | .solid {
200 | background: var(--color-link);
201 | }
202 |
203 | .pulse {
204 | background: var(--border-secondary)
205 | }
206 |
207 | .pulse {
208 | animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
209 | }
210 |
211 | .death {
212 | /* background: lavender !important; */
213 | border-color: violet !important;
214 | }
215 |
216 | @keyframes ping {
217 |
218 | 70%,
219 | 100% {
220 | transform: scale(2);
221 | opacity: 0;
222 | }
223 | }
224 |
225 |
226 | #summary div {
227 | padding: 1rem 2rem;
228 | border-radius: 2rem;
229 | background: var(--background-muted);
230 | text-align: center;
231 | /* border: 1px solid var(--border-secondary); */
232 | /* color: var(--background-primary); */
233 | margin-bottom: 1rem;
234 | }
235 |
236 | #summary {
237 | align-items: center;
238 | margin: 0 auto;
239 | }
240 |
241 | #summary h4 {
242 | text-align: center;
243 | }
244 |
245 | #summary h4:last-of-type {
246 | opacity: 0.5;
247 | }
248 |
249 | #summary p i {
250 | padding-right: 0.5rem;
251 | }
252 |
253 | footer {
254 | display: flex;
255 | flex-direction: column;
256 | gap: 1rem;
257 | align-items: center;
258 | padding: 3rem 0;
259 | padding-bottom: 5rem;
260 | }
261 |
262 | footer a {
263 | display: flex;
264 | align-items: center;
265 | gap: 0.5rem;
266 | padding: 0.3rem;
267 | padding-right: 0.5rem;
268 | background: var(--background-muted);
269 | border-radius: 4rem;
270 | border: 1px solid transparent;
271 | transition-duration: 200ms;
272 | text-decoration: none;
273 | }
274 |
275 | footer a:hover {
276 | border: 1px solid var(--color-link);
277 | }
278 |
279 | footer a img {
280 | max-width: 30px;
281 | aspect-ratio: 1/1;
282 | border-radius: 100%;
283 | }
284 |
285 | .portal-container {
286 | position: fixed;
287 | left: 0;
288 | top: 0;
289 | width: 100vw;
290 | height: 100vh;
291 | z-index: 1001;
292 | }
293 |
294 | .portal-underlay {
295 | position: absolute;
296 | inset: 0;
297 | z-index: 0;
298 | opacity: 0.7;
299 | background: var(--background-primary);
300 | border: none;
301 | padding: none;
302 | }
303 |
304 | .portal-content {
305 | position: absolute;
306 | /* pointer-events: none; */
307 | z-index: 1005;
308 | top: 50%;
309 | left: 50%;
310 | transform: translate(-50%, -50%);
311 | width: 600px;
312 | max-width: 90vw;
313 | max-height: 80vh;
314 | display: grid;
315 | place-items: center;
316 | background: var(--background-primary);
317 | }
318 |
319 | #form {
320 | width: 100%;
321 | padding: 2rem;
322 | display: flex;
323 | flex-direction: column;
324 | gap: 1rem;
325 | }
326 |
327 | #form>div:first-of-type {
328 | display: flex;
329 | align-items: center;
330 | justify-content: space-between;
331 | gap: 1rem;
332 | }
333 |
334 | #form .bday {
335 | display: grid;
336 | grid-template-columns: repeat(3, minmax(0, 1fr));
337 | gap: 1rem;
338 | }
339 |
340 | @media (min-width: 640px) {
341 | .btns-container {
342 | width: fit-content;
343 | }
344 |
345 | .progress-bar .bar-label {
346 | display: inline;
347 | }
348 |
349 | .clocks-grid {
350 | grid-template-columns: repeat(3, minmax(0, 1fr));
351 | }
352 |
353 | .dozen-grid {
354 | gap: 1rem;
355 | }
356 |
357 | .year-grid {
358 | gap: 0.2rem 0;
359 | }
360 |
361 | .dot {
362 | height: 0.3rem;
363 | }
364 |
365 | }
366 |
367 | @media (min-width: 768px) {
368 | .clocks-grid {
369 | grid-template-columns: repeat(4, minmax(0, 1fr));
370 | }
371 |
372 | .dozen-grid {
373 | gap: 1.25rem;
374 | }
375 |
376 | .year-grid {
377 | gap: 0.3rem 0;
378 | }
379 |
380 | .dot {
381 | height: 0.4rem;
382 | }
383 | }
--------------------------------------------------------------------------------
/reactjs-tutorial/src/index.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 800px;
3 | width: 100%;
4 | margin: 0 auto;
5 | display: flex;
6 | flex-direction: column;
7 | gap: 2rem;
8 | min-height: 100vh;
9 | }
10 |
11 | header,
12 | main,
13 | footer {
14 | padding: 1rem;
15 | }
16 |
17 | main {
18 | flex: 1;
19 | gap: 4rem;
20 | }
21 |
22 | section,
23 | main {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | section {
29 | gap: 1.5rem;
30 | }
31 |
32 | .btns-container {
33 | display: flex;
34 | align-items: center;
35 | gap: 0.5rem;
36 | padding: 0.5rem 0;
37 | }
38 |
39 | .btns-container button {
40 | flex: 1;
41 | white-space: nowrap;
42 | }
43 |
44 | .progress-bar {
45 | border-radius: 4rem;
46 | border: 2px solid var(--color-link);
47 | }
48 |
49 | .progress-bar>div {
50 | padding: 0.8rem 1.4rem;
51 | }
52 |
53 | .progress-bar>div,
54 | .progress-bar {
55 | display: flex;
56 | align-items: center;
57 | justify-content: space-between;
58 | gap: 1rem;
59 | overflow: hidden;
60 | min-width: fit-content;
61 | }
62 |
63 | .progress-bar>div:first-of-type {
64 | min-width: fit-content;
65 | background: var(--color-link);
66 | color: var(--background-primary);
67 | }
68 |
69 | .progress-bar div div {
70 | display: flex;
71 | align-items: center;
72 | gap: 1rem;
73 | }
74 |
75 | .progress-bar .bar-label {
76 | display: none;
77 | }
78 |
79 | .clocks-grid {
80 | display: grid;
81 | grid-template-columns: repeat(2, minmax(0, 1fr));
82 | gap: 1rem;
83 | }
84 |
85 |
86 | .clock-card {
87 | display: flex;
88 | flex-direction: column;
89 | align-items: center;
90 | justify-content: center;
91 | gap: 0.5rem;
92 | }
93 |
94 | .clock-card>div:last-of-type {
95 | display: flex;
96 | flex-direction: column;
97 | align-items: center;
98 | }
99 |
100 | .clock-card>div:last-of-type p {
101 | text-transform: lowercase;
102 | opacity: 0.7;
103 | }
104 |
105 | .circle {
106 | height: 4rem;
107 | aspect-ratio: 1/1;
108 | margin: 0.5rem 0;
109 | border-radius: 100%;
110 | border: 3px solid var(--border-primary);
111 | position: relative;
112 | }
113 |
114 | .circle .ticker {
115 | height: 50%;
116 | width: 3px;
117 | background: var(--border-secondary);
118 | border-radius: 1rem;
119 | position: absolute;
120 | top: 0;
121 | left: 50%;
122 | transform: translateX(-50%);
123 | transform-origin: bottom;
124 | }
125 |
126 | .years,
127 | .weeks,
128 | .days,
129 | .hours,
130 | .minutes,
131 | .seconds {
132 | animation: spin linear infinite;
133 | }
134 |
135 |
136 | .years {
137 | animation-duration: 31556952s;
138 | /* 1 year */
139 | }
140 |
141 | .months {
142 | animation-duration: 2629746s;
143 | /* 1 month */
144 | }
145 |
146 | .weeks {
147 | animation-duration: 604800s;
148 | /* 1 week */
149 | }
150 |
151 | .days {
152 | animation-duration: 86400s;
153 | /* 1 day */
154 | }
155 |
156 | .hours {
157 | animation-duration: 3600s;
158 | /* 1 hour */
159 | }
160 |
161 | .minutes {
162 | animation-duration: 60s;
163 | /* 1 minute */
164 | }
165 |
166 | .seconds {
167 | animation-duration: 1s;
168 | /* 1 minute */
169 | }
170 |
171 | @keyframes spin {
172 | from {
173 | transform: rotate(0deg);
174 | }
175 |
176 | to {
177 | transform: rotate(360deg);
178 | }
179 | }
180 |
181 | .dozen-grid {
182 | display: grid;
183 | grid-template-columns: repeat(12, minmax(0, 1fr));
184 | gap: 0.5rem;
185 | }
186 |
187 | .year-grid {
188 | display: grid;
189 | grid-template-columns: repeat(4, minmax(0, 1fr));
190 | gap: 0.15rem 0;
191 | }
192 |
193 | .dot {
194 | height: 0.2rem;
195 | aspect-ratio: 1/1;
196 | border-radius: 0.2rem;
197 | border: 0.5px solid var(--color-link);
198 | }
199 |
200 | .solid {
201 | background: var(--color-link);
202 | }
203 |
204 | .pulse {
205 | background: var(--border-secondary)
206 | }
207 |
208 | .pulse {
209 | animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
210 | }
211 |
212 | .death {
213 | /* background: lavender !important; */
214 | border-color: violet !important;
215 | }
216 |
217 | @keyframes ping {
218 |
219 | 70%,
220 | 100% {
221 | transform: scale(2);
222 | opacity: 0;
223 | }
224 | }
225 |
226 |
227 | #summary div {
228 | padding: 1rem 2rem;
229 | border-radius: 2rem;
230 | background: var(--background-muted);
231 | text-align: center;
232 | /* border: 1px solid var(--border-secondary); */
233 | /* color: var(--background-primary); */
234 | margin-bottom: 1rem;
235 | }
236 |
237 | #summary {
238 | align-items: center;
239 | margin: 0 auto;
240 | }
241 |
242 | #summary h4 {
243 | text-align: center;
244 | }
245 |
246 | #summary h4:last-of-type {
247 | opacity: 0.5;
248 | }
249 |
250 | #summary p i {
251 | padding-right: 0.5rem;
252 | }
253 |
254 | footer {
255 | display: flex;
256 | flex-direction: column;
257 | gap: 1rem;
258 | align-items: center;
259 | padding: 3rem 0;
260 | padding-bottom: 5rem;
261 | }
262 |
263 | footer a {
264 | display: flex;
265 | align-items: center;
266 | gap: 0.5rem;
267 | padding: 0.3rem;
268 | padding-right: 0.5rem;
269 | background: var(--background-muted);
270 | border-radius: 4rem;
271 | border: 1px solid transparent;
272 | transition-duration: 200ms;
273 | text-decoration: none;
274 | }
275 |
276 | footer a:hover {
277 | border: 1px solid var(--color-link);
278 | }
279 |
280 | footer a img {
281 | max-width: 30px;
282 | aspect-ratio: 1/1;
283 | border-radius: 100%;
284 | }
285 |
286 | .portal-container {
287 | position: fixed;
288 | left: 0;
289 | top: 0;
290 | width: 100vw;
291 | height: 100vh;
292 | z-index: 1001;
293 | }
294 |
295 | .portal-underlay {
296 | position: absolute;
297 | inset: 0;
298 | z-index: 0;
299 | opacity: 0.7;
300 | background: var(--background-primary);
301 | border: none;
302 | padding: none;
303 | }
304 |
305 | .portal-content {
306 | position: absolute;
307 | /* pointer-events: none; */
308 | z-index: 1005;
309 | top: 50%;
310 | left: 50%;
311 | transform: translate(-50%, -50%);
312 | width: 600px;
313 | max-width: 90vw;
314 | max-height: 80vh;
315 | display: grid;
316 | place-items: center;
317 | background: var(--background-primary);
318 | }
319 |
320 | #form {
321 | width: 100%;
322 | padding: 2rem;
323 | display: flex;
324 | flex-direction: column;
325 | gap: 1rem;
326 | }
327 |
328 | #form>div:first-of-type {
329 | display: flex;
330 | align-items: center;
331 | justify-content: space-between;
332 | gap: 1rem;
333 | }
334 |
335 | #form .bday {
336 | display: grid;
337 | grid-template-columns: repeat(3, minmax(0, 1fr));
338 | gap: 1rem;
339 | }
340 |
341 | @media (min-width: 640px) {
342 | .btns-container {
343 | width: fit-content;
344 | }
345 |
346 | .progress-bar .bar-label {
347 | display: inline;
348 | }
349 |
350 | .clocks-grid {
351 | grid-template-columns: repeat(3, minmax(0, 1fr));
352 | }
353 |
354 | .dozen-grid {
355 | gap: 1rem;
356 | }
357 |
358 | .year-grid {
359 | gap: 0.2rem 0;
360 | }
361 |
362 | .dot {
363 | height: 0.3rem;
364 | }
365 |
366 | }
367 |
368 | @media (min-width: 768px) {
369 | .clocks-grid {
370 | grid-template-columns: repeat(4, minmax(0, 1fr));
371 | }
372 |
373 | .dozen-grid {
374 | gap: 1.25rem;
375 | }
376 |
377 | .year-grid {
378 | gap: 0.3rem 0;
379 | }
380 |
381 | .dot {
382 | height: 0.4rem;
383 | }
384 | }
--------------------------------------------------------------------------------
/svelte-tutorial/src/index.css:
--------------------------------------------------------------------------------
1 | #app {
2 | max-width: 800px;
3 | width: 100%;
4 | margin: 0 auto;
5 | display: flex;
6 | flex-direction: column;
7 | gap: 2rem;
8 | min-height: 100vh;
9 | }
10 |
11 | header,
12 | main,
13 | footer {
14 | padding: 1rem;
15 | }
16 |
17 | main {
18 | flex: 1;
19 | gap: 4rem;
20 | }
21 |
22 | section,
23 | main {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | section {
29 | gap: 1.5rem;
30 | }
31 |
32 | .btns-container {
33 | display: flex;
34 | align-items: center;
35 | gap: 0.5rem;
36 | padding: 0.5rem 0;
37 | }
38 |
39 | .btns-container button {
40 | flex: 1;
41 | white-space: nowrap;
42 | }
43 |
44 | .progress-bar {
45 | border-radius: 4rem;
46 | border: 2px solid var(--color-link);
47 | }
48 |
49 | .progress-bar>div {
50 | padding: 0.8rem 1.4rem;
51 | }
52 |
53 | .progress-bar>div,
54 | .progress-bar {
55 | display: flex;
56 | align-items: center;
57 | justify-content: space-between;
58 | gap: 1rem;
59 | overflow: hidden;
60 | min-width: fit-content;
61 | }
62 |
63 | .progress-bar>div:first-of-type {
64 | min-width: fit-content;
65 | background: var(--color-link);
66 | color: var(--background-primary);
67 | }
68 |
69 | .progress-bar div div {
70 | display: flex;
71 | align-items: center;
72 | gap: 1rem;
73 | }
74 |
75 | .progress-bar .bar-label {
76 | display: none;
77 | }
78 |
79 | .clocks-grid {
80 | display: grid;
81 | grid-template-columns: repeat(2, minmax(0, 1fr));
82 | gap: 1rem;
83 | }
84 |
85 |
86 | .clock-card {
87 | display: flex;
88 | flex-direction: column;
89 | align-items: center;
90 | justify-content: center;
91 | gap: 0.5rem;
92 | }
93 |
94 | .clock-card>div:last-of-type {
95 | display: flex;
96 | flex-direction: column;
97 | align-items: center;
98 | }
99 |
100 | .clock-card>div:last-of-type p {
101 | text-transform: lowercase;
102 | opacity: 0.7;
103 | }
104 |
105 | .circle {
106 | height: 4rem;
107 | aspect-ratio: 1/1;
108 | margin: 0.5rem 0;
109 | border-radius: 100%;
110 | border: 3px solid var(--border-primary);
111 | position: relative;
112 | }
113 |
114 | .circle .ticker {
115 | height: 50%;
116 | width: 3px;
117 | background: var(--border-secondary);
118 | border-radius: 1rem;
119 | position: absolute;
120 | top: 0;
121 | left: 50%;
122 | transform: translateX(-50%);
123 | transform-origin: bottom;
124 | }
125 |
126 | .years,
127 | .weeks,
128 | .days,
129 | .hours,
130 | .minutes,
131 | .seconds {
132 | animation: spin linear infinite;
133 | }
134 |
135 |
136 | .years {
137 | animation-duration: 31556952s;
138 | /* 1 year */
139 | }
140 |
141 | .months {
142 | animation-duration: 2629746s;
143 | /* 1 month */
144 | }
145 |
146 | .weeks {
147 | animation-duration: 604800s;
148 | /* 1 week */
149 | }
150 |
151 | .days {
152 | animation-duration: 86400s;
153 | /* 1 day */
154 | }
155 |
156 | .hours {
157 | animation-duration: 3600s;
158 | /* 1 hour */
159 | }
160 |
161 | .minutes {
162 | animation-duration: 60s;
163 | /* 1 minute */
164 | }
165 |
166 | .seconds {
167 | animation-duration: 1s;
168 | /* 1 minute */
169 | }
170 |
171 | @keyframes spin {
172 | from {
173 | transform: rotate(0deg);
174 | }
175 |
176 | to {
177 | transform: rotate(360deg);
178 | }
179 | }
180 |
181 | .dozen-grid {
182 | display: grid;
183 | grid-template-columns: repeat(12, minmax(0, 1fr));
184 | gap: 0.5rem;
185 | }
186 |
187 | .year-grid {
188 | display: grid;
189 | grid-template-columns: repeat(4, minmax(0, 1fr));
190 | gap: 0.15rem 0;
191 | }
192 |
193 | .dot {
194 | height: 0.2rem;
195 | aspect-ratio: 1/1;
196 | border-radius: 0.2rem;
197 | border: 0.5px solid var(--color-link);
198 | }
199 |
200 | .solid {
201 | background: var(--color-link);
202 | }
203 |
204 | .pulse {
205 | background: var(--border-secondary)
206 | }
207 |
208 | .pulse {
209 | animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
210 | }
211 |
212 | .death {
213 | /* background: lavender !important; */
214 | border-color: violet !important;
215 | }
216 |
217 | @keyframes ping {
218 |
219 | 70%,
220 | 100% {
221 | transform: scale(2);
222 | opacity: 0;
223 | }
224 | }
225 |
226 |
227 | #summary div {
228 | padding: 1rem 2rem;
229 | border-radius: 2rem;
230 | background: var(--background-muted);
231 | text-align: center;
232 | /* border: 1px solid var(--border-secondary); */
233 | /* color: var(--background-primary); */
234 | margin-bottom: 1rem;
235 | }
236 |
237 | #summary {
238 | align-items: center;
239 | margin: 0 auto;
240 | }
241 |
242 | #summary h4 {
243 | text-align: center;
244 | }
245 |
246 | #summary h4:last-of-type {
247 | opacity: 0.5;
248 | }
249 |
250 | #summary p i {
251 | padding-right: 0.5rem;
252 | }
253 |
254 | footer {
255 | display: flex;
256 | flex-direction: column;
257 | gap: 1rem;
258 | align-items: center;
259 | padding: 3rem 0;
260 | padding-bottom: 5rem;
261 | }
262 |
263 | footer a {
264 | display: flex;
265 | align-items: center;
266 | gap: 0.5rem;
267 | padding: 0.3rem;
268 | padding-right: 0.5rem;
269 | background: var(--background-muted);
270 | border-radius: 4rem;
271 | border: 1px solid transparent;
272 | transition-duration: 200ms;
273 | text-decoration: none;
274 | }
275 |
276 | footer a:hover {
277 | border: 1px solid var(--color-link);
278 | }
279 |
280 | footer a img {
281 | max-width: 30px;
282 | aspect-ratio: 1/1;
283 | border-radius: 100%;
284 | }
285 |
286 | .portal-container {
287 | position: fixed;
288 | left: 0;
289 | top: 0;
290 | width: 100vw;
291 | height: 100vh;
292 | z-index: 1001;
293 | }
294 |
295 | .portal-underlay {
296 | position: absolute;
297 | inset: 0;
298 | z-index: 0;
299 | opacity: 0.7;
300 | background: var(--background-primary);
301 | border: none;
302 | padding: none;
303 | }
304 |
305 | .portal-content {
306 | position: absolute;
307 | /* pointer-events: none; */
308 | z-index: 1005;
309 | top: 50%;
310 | left: 50%;
311 | transform: translate(-50%, -50%);
312 | width: 600px;
313 | max-width: 90vw;
314 | max-height: 80vh;
315 | display: grid;
316 | place-items: center;
317 | background: var(--background-primary);
318 | }
319 |
320 | #form {
321 | width: 100%;
322 | padding: 2rem;
323 | display: flex;
324 | flex-direction: column;
325 | gap: 1rem;
326 | }
327 |
328 | #form>div:first-of-type {
329 | display: flex;
330 | align-items: center;
331 | justify-content: space-between;
332 | gap: 1rem;
333 | }
334 |
335 | #form .bday {
336 | display: grid;
337 | grid-template-columns: repeat(3, minmax(0, 1fr));
338 | gap: 1rem;
339 | }
340 |
341 | @media (min-width: 640px) {
342 | .btns-container {
343 | width: fit-content;
344 | }
345 |
346 | .progress-bar .bar-label {
347 | display: inline;
348 | }
349 |
350 | .clocks-grid {
351 | grid-template-columns: repeat(3, minmax(0, 1fr));
352 | }
353 |
354 | .dozen-grid {
355 | gap: 1rem;
356 | }
357 |
358 | .year-grid {
359 | gap: 0.2rem 0;
360 | }
361 |
362 | .dot {
363 | height: 0.3rem;
364 | }
365 |
366 | }
367 |
368 | @media (min-width: 768px) {
369 | .clocks-grid {
370 | grid-template-columns: repeat(4, minmax(0, 1fr));
371 | }
372 |
373 | .dozen-grid {
374 | gap: 1.25rem;
375 | }
376 |
377 | .year-grid {
378 | gap: 0.3rem 0;
379 | }
380 |
381 | .dot {
382 | height: 0.4rem;
383 | }
384 | }
--------------------------------------------------------------------------------
/vue-tutorial/src/fanta.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Fanta.css (Fantasy + CSS)
3 | * Version 0.1.0
4 | * https://github.com/jamezmca/fantacss
5 | *
6 | * Sections
7 | * 1. Content sectioning
8 | * 2. Text content
9 | * 3. Inline text semantics
10 | * 4. Image and multimedia
11 | * 5. Tables
12 | * 6. Forms
13 | * 7. Interactive elements
14 | *
15 | */
16 |
17 | /* @import url('https://fonts.googleapis.com/css2?family=Eczar:wght@400..800&family=Grenze:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); */
18 | @import url('https://fonts.googleapis.com/css2?family=Eczar:wght@400..800&family=Press+Start+2P&display=swap');
19 |
20 | /* :root {
21 | --background-primary: white;
22 | --background-secondary: '';
23 | --background-tertiary: #dbeafe;
24 | --background-accent: '';
25 | --background-compliment: '';
26 | --background-gradient: '';
27 | --background-muted: #f8fafc;
28 |
29 | --color-primary: #030615;
30 | --color-secondary: '';
31 | --color-tertiary: '';
32 | --color-accent: '';
33 | --color-compliment: '';
34 | --color-gradient: '';
35 | --color-muted: ;
36 | --color-link: #2563eb;
37 | --color-link-transparent: rgba(37, 99, 235, 0.1);
38 |
39 | --color-success: '';
40 | --color-warning: '';
41 | --color-error: '';
42 | --color-info: '';
43 | --color-highlight: #fef9c3;
44 |
45 | --gradient-start: #9580ff;
46 | --gradient-end: #80ffea;
47 |
48 | --border-primary: #f1f5f9;
49 | --border-secondary: #bed1e7;
50 | --border-highlight: #64748b;
51 | --border-tertiary: '';
52 |
53 | --shadow-dark: '';
54 | --shadow-light: '';
55 | --shadow-text: '';
56 |
57 | --padding-small: 1rem;
58 | --padding-large: 2rem;
59 |
60 | --border-radius-small: 0.5rem;
61 | --border-radius-large: 0.75rem;
62 | --highlight-border-radius: 0.5rem;
63 |
64 | --text-selection: '';
65 | } */
66 |
67 | /* @media (prefers-color-scheme: dark) { */
68 | :root {
69 | --background-primary: #030615;
70 | --background-secondary: rgb(23 37 84);
71 | --background-tertiary: #121424;
72 | --background-accent: '';
73 | --background-compliment: '';
74 | --background-gradient: '';
75 | --background-muted: #1a1e32;
76 |
77 | --color-primary: rgb(219 234 254);
78 | --color-secondary: '';
79 | --color-tertiary: '';
80 | --color-accent: '';
81 | --color-compliment: '';
82 | --color-gradient: '';
83 | --color-muted: ;
84 | --color-link: #60a5fa;
85 | --color-link-transparent: rgba(37, 99, 235, 0.1);
86 |
87 | --color-success: '';
88 | --color-warning: '';
89 | --color-error: '';
90 | --color-info: '';
91 | --color-highlight: #fef9c3;
92 |
93 | --gradient-start: #0084ff;
94 | --gradient-end: #00fffb;
95 |
96 | --border-primary: #29325b;
97 | --border-secondary: #93c5fd;
98 | --border-highlight: #4649af;
99 | --border-tertiary: '';
100 |
101 | --shadow-dark: '';
102 | --shadow-light: '';
103 | --shadow-text: '';
104 |
105 | --padding-small: 1rem;
106 | --padding-large: 2rem;
107 |
108 | --border-radius-small: 0.5rem;
109 | --border-radius-large: 0.75rem;
110 | --highlight-border-radius: 0.5rem;
111 |
112 | --text-selection: '';
113 | }
114 |
115 | /* } */
116 |
117 | * {
118 | margin: 0;
119 | padding: 0;
120 | box-sizing: border-box;
121 | font-family: "Eczar", serif;
122 | }
123 |
124 | body {
125 | background: var(--background-primary);
126 | color: var(--color-primary);
127 | font-size: 0.875rem;
128 | line-height: 1.6rem;
129 | }
130 |
131 | /* Special */
132 | .text-gradient {
133 | -webkit-text-fill-color: transparent;
134 | -webkit-background-clip: text;
135 | background-image: linear-gradient(180deg, var(--gradient-start) 0, var(--gradient-end) 100%);
136 | background-size: 100%;
137 | -webkit-box-decoration-break: clone;
138 | }
139 |
140 |
141 | /* Typography */
142 | h1,
143 | h2,
144 | h3,
145 | h4,
146 | h5,
147 | h6,
148 | button {
149 | font-family: "Press Start 2P", serif;
150 | width: fit-content;
151 | }
152 |
153 | /* h1, */
154 | .text-large {
155 | font-size: 1.875rem;
156 | line-height: 2.25rem;
157 | }
158 |
159 | h1 {
160 | font-size: 1.5rem;
161 | line-height: 2rem;
162 | }
163 |
164 | .special-shadow {
165 | text-shadow: -1px -1px 4px #0022ffbf, 2px 2px 10px #0022ffbf, 0 0 20px #0022ffbf;
166 | color: white;
167 | }
168 |
169 | h2 {
170 | font-size: 1.25rem;
171 | line-height: 1.75rem;
172 | }
173 |
174 | h3,
175 | .text-medium {
176 | font-size: 1.125rem;
177 | line-height: 1.75rem;
178 | }
179 |
180 | h4 {
181 | font-size: 1rem;
182 | line-height: 1.5rem;
183 | }
184 |
185 | h5,
186 | h6 {
187 | font-size: 0.875rem;
188 | line-height: 1.25rem;
189 | }
190 |
191 | p {
192 | display: block;
193 | width: fit-content;
194 | }
195 |
196 | span {
197 | font-weight: inherit;
198 | font-size: inherit;
199 | line-height: inherit;
200 | }
201 |
202 | /* Content Sectioning */
203 |
204 | main {}
205 |
206 | header {}
207 |
208 | footer {}
209 |
210 | nav {}
211 |
212 | section {}
213 |
214 | address {
215 | font-style: normal;
216 | }
217 |
218 | aside {
219 | float: right;
220 | width: 40%;
221 | padding: 0.75rem;
222 | margin: 0.5rem;
223 | font-style: italic;
224 | color: var(--color-primary);
225 | background-color: var(--background-muted);
226 | border-radius: var(--border-radius-large);
227 | }
228 |
229 |
230 | /* Text Content */
231 | blockquote {
232 | position: relative;
233 | padding-left: 1.5rem;
234 | margin: 0;
235 | }
236 |
237 | blockquote::after {
238 | content: "";
239 | display: block;
240 | position: absolute;
241 | left: 0;
242 | top: 0;
243 | height: 100%;
244 | border-left: 7px solid var(--border-primary);
245 | border-radius: 6px;
246 | }
247 |
248 | blockquote footer {
249 | padding-top: 1rem;
250 | }
251 |
252 | dd {
253 | padding-bottom: 11px;
254 | }
255 |
256 | dl {}
257 |
258 | dt {
259 | font-weight: bold;
260 | }
261 |
262 | figure {}
263 |
264 | figcaption {
265 | padding-top: 10px;
266 | font-size: 0.8rem;
267 | }
268 |
269 | hr {}
270 |
271 |
272 | ul,
273 | ol {
274 | list-style-position: inside;
275 | padding-left: 1rem;
276 | }
277 |
278 | li {
279 | line-height: 1.6em;
280 | }
281 |
282 | /* Inline Text Elements */
283 |
284 | a {
285 | color: var(--color-link);
286 | }
287 |
288 | a:active,
289 | a:focus,
290 | a:hover {
291 | text-decoration: none;
292 | }
293 |
294 | .link-button {
295 | border: none;
296 | color: var(--color-primary);
297 | background: var(--background-primary);
298 | box-shadow: none;
299 | padding: 0.5rem 1rem;
300 | }
301 |
302 | .link-button:hover {
303 | box-shadow: none;
304 | transform: unset;
305 | text-decoration: underline;
306 | }
307 |
308 | mark,
309 | samp,
310 | kbd,
311 | code,
312 | time {
313 | border-radius: var(--highlight-border-radius, 4px);
314 | box-decoration-break: clone;
315 | -webkit-box-decoration-break: clone;
316 | }
317 |
318 | mark {
319 | background-color: var(--color-highlight);
320 | padding: 0 4px;
321 | }
322 |
323 | samp {
324 | font-weight: bold;
325 | padding: 0.5rem 1rem;
326 | background-color: var(--background-muted);
327 | color: var(--color-primary);
328 | }
329 |
330 | kbd,
331 | time {
332 | padding: 0rem 0.5rem;
333 | background-color: var(--background-muted);
334 | color: var(--color-primary);
335 | }
336 |
337 | code,
338 | pre {
339 | font-size: 0.9em;
340 | padding: 0.2rem 0.5rem;
341 | background: var(--background-muted);
342 | border: 1px solid var(--border-primary);
343 | max-width: fit-content;
344 | overflow-x: auto;
345 | }
346 |
347 | pre>code {
348 | padding: 10px;
349 | border: 0;
350 | display: block;
351 | overflow-x: auto;
352 | }
353 |
354 | pre {
355 | border-radius: var(--border-radius-large);
356 | }
357 |
358 | sup,
359 | sub {
360 | line-height: normal;
361 | }
362 |
363 | /* Image and multimedia */
364 | audio {
365 | width: 100%;
366 | }
367 |
368 | audio,
369 | img,
370 | video {
371 | border-radius: var(--border-radius-large);
372 | max-width: 100%;
373 | }
374 |
375 | img {
376 | height: auto;
377 | }
378 |
379 | /* Tables */
380 | table {
381 | width: fit-content;
382 | border: 1px solid var(--border-primary);
383 | background: var(--background-muted);
384 | border-radius: var(--border-radius-small);
385 | }
386 |
387 | table tr:last-child td:first-child {
388 | border-bottom-left-radius: 8px;
389 | }
390 |
391 | table tr:last-child td:last-child {
392 | border-bottom-right-radius: 8px;
393 | }
394 |
395 | table tr:first-child th:first-child {
396 | border-top-left-radius: 8px;
397 | }
398 |
399 | table tr:first-child th:last-child {
400 | border-top-right-radius: 8px;
401 | }
402 |
403 | th {
404 | background-color: var(--background-muted);
405 | }
406 |
407 | td {
408 | background: var(--background-primary);
409 | }
410 |
411 | td,
412 | th {
413 | text-align: left;
414 | padding: 8px;
415 | }
416 |
417 | thead {
418 | border-collapse: collapse;
419 | }
420 |
421 | tfoot {
422 | border-top: 1px solid black;
423 | }
424 |
425 | table tr:hover td,
426 | tbody tr:nth-child(even):hover td {
427 | background-color: var(--background-muted);
428 | }
429 |
430 | /* Form elements */
431 | input,
432 | button,
433 | select,
434 | optgroup,
435 | textarea {
436 | /* margin: 0; */
437 | }
438 |
439 | button,
440 | select,
441 | input[type="submit"],
442 | input[type="button"],
443 | input[type="checkbox"],
444 | input[type="range"],
445 | input[type="radio"] {
446 | cursor: pointer;
447 | }
448 |
449 | button {
450 | color: var(--color-primary);
451 | background-color: var(--background-secondary);
452 | width: fit-content;
453 | font-family: inherit;
454 | font-size: inherit;
455 | font-weight: 500;
456 | padding: 0.25rem 1.25rem;
457 | border: 1.5px solid var(--border-secondary);
458 | border-radius: var(--border-radius-small);
459 | box-shadow: 2px 2px 0 0 var(--border-secondary);
460 | transition-duration: 200ms;
461 | }
462 |
463 | button:hover {
464 | box-shadow: 0 0 0 0 var(--border-secondary);
465 | transform: translate(2px, 2px);
466 | }
467 |
468 | button[disabled]:hover {
469 | box-shadow: 2px 2px 0 0 var(--border-secondary);
470 | transform: translate(0, 0);
471 | }
472 |
473 | button:disabled,
474 | button[disabled] {
475 | border: 1.5px solid var(--border-secondary);
476 | cursor: initial;
477 | opacity: 0.55;
478 | }
479 |
480 | label {
481 | display: block;
482 | max-width: fit-content;
483 | font-weight: 500;
484 | }
485 |
486 | input,
487 | textarea,
488 | select {
489 | font-size: 1em;
490 | background-color: var(--background-muted);
491 | border: 1px solid var(--border-secondary);
492 | color: var(--color-primary);
493 | padding: 0.5rem 0.75rem;
494 | border-radius: var(--border-radius-small);
495 | width: 100%;
496 | /* max-width: fit-content; */
497 | outline: none;
498 | appearance: none;
499 | }
500 |
501 | input:disabled {
502 | cursor: not-allowed;
503 | opacity: 0.6;
504 | }
505 |
506 | input[type="number"].buttonless {
507 | appearance: textfield;
508 | -moz-appearance: textfield;
509 | }
510 |
511 | input[type="number"].buttonless::-webkit-inner-spin-button {
512 | display: none;
513 | appearance: none;
514 | }
515 |
516 | input[type="number"].buttonless::-webkit-outer-spin-button {
517 | -webkit-appearance: none;
518 | margin: 0;
519 | }
520 |
521 | input[type="checkbox"],
522 | input[type="radio"] {
523 | padding: 0.5rem;
524 | width: fit-content;
525 | vertical-align: middle;
526 | position: relative;
527 | margin: 0.5rem 0.25rem 0.5rem 0.5rem;
528 | /* margin-right: 0.33em;
529 | margin-top: 0.31em; */
530 | }
531 |
532 | input[type="checkbox"] {
533 | border-radius: 7px;
534 | margin-left: 0;
535 | }
536 |
537 | input[type="radio"] {
538 | border-radius: 100%;
539 | }
540 |
541 | input[type="checkbox"]:checked,
542 | input[type="radio"]:checked {
543 | background: var(--border-secondary);
544 | }
545 |
546 | input[type="range"] {
547 | vertical-align: middle;
548 | padding: 0;
549 | }
550 |
551 | input[type="color"] {
552 | appearance: none;
553 | border: none;
554 | outline-style: none;
555 | padding: initial;
556 | max-width: initial;
557 | height: 2rem;
558 | width: 3rem;
559 | }
560 |
561 | select:is([multiple]) {
562 | background: none;
563 | height: fit-content;
564 | }
565 |
566 | fieldset:focus-within,
567 | input:focus-within,
568 | textarea:focus-within,
569 | select:focus-within {
570 | border-color: var(--border-highlight);
571 | }
572 |
573 | fieldset:hover,
574 | input:hover,
575 | textarea:hover,
576 | select:hover {
577 | border-color: var(--border-highlight);
578 | }
579 |
580 | progress {
581 | appearance: none;
582 | height: 1rem;
583 | margin: 0.75rem 0;
584 | }
585 |
586 | progress::-webkit-progress-bar {
587 | background: var(--background-main);
588 | border: 1px solid var(--border-primary);
589 | border-radius: var(--highlight-border-radius);
590 | }
591 |
592 | progress::-webkit-progress-value {
593 | background-color: var(--color-link);
594 | border-radius: var(--border-radius-small);
595 | }
596 |
597 | progress::-moz-progress-bar {
598 | background-color: var(--color-link);
599 | border-radius: var(--border-radius-small);
600 | }
601 |
602 | fieldset {
603 | border: 1px solid var(--border-primary);
604 | border-radius: var(--border-radius-small);
605 | margin: 0;
606 | margin-bottom: 6px;
607 | padding: 1rem;
608 | max-width: fit-content;
609 | }
610 |
611 | /* Interactive elements */
612 | details {
613 | border: 1px solid var(--border-primary);
614 | border-radius: var(--border-radius-small);
615 | padding: 0.5rem 0.75rem;
616 | }
617 |
618 | summary {
619 | font-weight: bold;
620 | }
621 |
622 | details[open] summary {
623 | border-bottom: 1px solid var(--border-primary);
624 | margin-bottom: 0.5rem;
625 | }
626 |
627 | .card,
628 | .button-card {
629 | background-color: var(--background-muted);
630 | color: var(--color-primary);
631 | padding: 1rem;
632 | border-radius: 0.5rem;
633 | }
634 |
635 | .card {
636 | border: 1px solid var(--color-link-transparent);
637 | }
638 |
639 | .button-card {
640 | border: 1px solid var(--border-secondary);
641 | }
642 |
643 | .button-card {
644 | box-shadow: none;
645 | }
646 |
647 | .button-card:hover {
648 | transform: translate(0);
649 | box-shadow: none;
650 | border-color: var(--border-highlight);
651 | }
652 |
653 | .card-button-primary,
654 | .card-button-secondary {
655 | border: none !important;
656 | box-shadow: none !important;
657 | }
658 |
659 | .card-button-primary {
660 | color: var(--background-primary);
661 | background: var(--color-link);
662 | }
663 |
664 | .card-button-secondary {
665 | color: var(--color-link);
666 | background: var(--color-link-transparent);
667 | }
668 |
669 | .card-button-primary:hover,
670 | .card-button-secondary:hover {
671 | transform: none;
672 | opacity: 0.6;
673 | }
674 |
675 |
676 | @media (min-width: 640px) {
677 | body {
678 | font-size: 1rem;
679 | line-height: 1.5rem;
680 | }
681 |
682 | /* h1, */
683 | .text-large {
684 | font-size: 2.25rem;
685 | line-height: 2.5rem;
686 | }
687 |
688 | h1 {
689 | font-size: 1.875rem;
690 | line-height: 2.25rem;
691 | }
692 |
693 | h2 {
694 | font-size: 1.5rem;
695 | line-height: 2rem;
696 | }
697 |
698 | h3,
699 | .text-medium {
700 | font-size: 1.25rem;
701 | line-height: 1.75rem;
702 | }
703 |
704 | h4 {
705 | font-size: 1.125rem;
706 | line-height: 1.75rem;
707 | }
708 |
709 | h5,
710 | h6 {
711 | font-size: 1rem;
712 | line-height: 1.5rem;
713 | }
714 |
715 | button {
716 | width: fit-content;
717 | }
718 |
719 | input {
720 | max-width: 600px;
721 | }
722 | }
--------------------------------------------------------------------------------
/reactjs-tutorial/src/fanta.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Fanta.css (Fantasy + CSS)
3 | * Version 0.1.0
4 | * https://github.com/jamezmca/fantacss
5 | *
6 | * Sections
7 | * 1. Content sectioning
8 | * 2. Text content
9 | * 3. Inline text semantics
10 | * 4. Image and multimedia
11 | * 5. Tables
12 | * 6. Forms
13 | * 7. Interactive elements
14 | *
15 | */
16 |
17 | /* @import url('https://fonts.googleapis.com/css2?family=Eczar:wght@400..800&family=Grenze:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); */
18 | @import url('https://fonts.googleapis.com/css2?family=Eczar:wght@400..800&family=Press+Start+2P&display=swap');
19 |
20 | /* :root {
21 | --background-primary: white;
22 | --background-secondary: '';
23 | --background-tertiary: #dbeafe;
24 | --background-accent: '';
25 | --background-compliment: '';
26 | --background-gradient: '';
27 | --background-muted: #f8fafc;
28 |
29 | --color-primary: #030615;
30 | --color-secondary: '';
31 | --color-tertiary: '';
32 | --color-accent: '';
33 | --color-compliment: '';
34 | --color-gradient: '';
35 | --color-muted: ;
36 | --color-link: #2563eb;
37 | --color-link-transparent: rgba(37, 99, 235, 0.1);
38 |
39 | --color-success: '';
40 | --color-warning: '';
41 | --color-error: '';
42 | --color-info: '';
43 | --color-highlight: #fef9c3;
44 |
45 | --gradient-start: #9580ff;
46 | --gradient-end: #80ffea;
47 |
48 | --border-primary: #f1f5f9;
49 | --border-secondary: #bed1e7;
50 | --border-highlight: #64748b;
51 | --border-tertiary: '';
52 |
53 | --shadow-dark: '';
54 | --shadow-light: '';
55 | --shadow-text: '';
56 |
57 | --padding-small: 1rem;
58 | --padding-large: 2rem;
59 |
60 | --border-radius-small: 0.5rem;
61 | --border-radius-large: 0.75rem;
62 | --highlight-border-radius: 0.5rem;
63 |
64 | --text-selection: '';
65 | } */
66 |
67 | /* @media (prefers-color-scheme: dark) { */
68 | :root {
69 | --background-primary: #030615;
70 | --background-secondary: rgb(23 37 84);
71 | --background-tertiary: #121424;
72 | --background-accent: '';
73 | --background-compliment: '';
74 | --background-gradient: '';
75 | --background-muted: #1a1e32;
76 |
77 | --color-primary: rgb(219 234 254);
78 | --color-secondary: '';
79 | --color-tertiary: '';
80 | --color-accent: '';
81 | --color-compliment: '';
82 | --color-gradient: '';
83 | --color-muted: ;
84 | --color-link: #60a5fa;
85 | --color-link-transparent: rgba(37, 99, 235, 0.1);
86 |
87 | --color-success: '';
88 | --color-warning: '';
89 | --color-error: '';
90 | --color-info: '';
91 | --color-highlight: #fef9c3;
92 |
93 | --gradient-start: #0084ff;
94 | --gradient-end: #00fffb;
95 |
96 | --border-primary: #29325b;
97 | --border-secondary: #93c5fd;
98 | --border-highlight: #4649af;
99 | --border-tertiary: '';
100 |
101 | --shadow-dark: '';
102 | --shadow-light: '';
103 | --shadow-text: '';
104 |
105 | --padding-small: 1rem;
106 | --padding-large: 2rem;
107 |
108 | --border-radius-small: 0.5rem;
109 | --border-radius-large: 0.75rem;
110 | --highlight-border-radius: 0.5rem;
111 |
112 | --text-selection: '';
113 | }
114 |
115 | /* } */
116 |
117 | * {
118 | margin: 0;
119 | padding: 0;
120 | box-sizing: border-box;
121 | font-family: "Eczar", serif;
122 | }
123 |
124 | body {
125 | background: var(--background-primary);
126 | color: var(--color-primary);
127 | font-size: 0.875rem;
128 | line-height: 1.6rem;
129 | }
130 |
131 | /* Special */
132 | .text-gradient {
133 | -webkit-text-fill-color: transparent;
134 | -webkit-background-clip: text;
135 | background-image: linear-gradient(180deg, var(--gradient-start) 0, var(--gradient-end) 100%);
136 | background-size: 100%;
137 | -webkit-box-decoration-break: clone;
138 | }
139 |
140 |
141 | /* Typography */
142 | h1,
143 | h2,
144 | h3,
145 | h4,
146 | h5,
147 | h6,
148 | button {
149 | font-family: "Press Start 2P", serif;
150 | width: fit-content;
151 | }
152 |
153 | /* h1, */
154 | .text-large {
155 | font-size: 1.875rem;
156 | line-height: 2.25rem;
157 | }
158 |
159 | h1 {
160 | font-size: 1.5rem;
161 | line-height: 2rem;
162 | }
163 |
164 | .special-shadow {
165 | text-shadow: -1px -1px 4px #0022ffbf, 2px 2px 10px #0022ffbf, 0 0 20px #0022ffbf;
166 | color: white;
167 | }
168 |
169 | h2 {
170 | font-size: 1.25rem;
171 | line-height: 1.75rem;
172 | }
173 |
174 | h3,
175 | .text-medium {
176 | font-size: 1.125rem;
177 | line-height: 1.75rem;
178 | }
179 |
180 | h4 {
181 | font-size: 1rem;
182 | line-height: 1.5rem;
183 | }
184 |
185 | h5,
186 | h6 {
187 | font-size: 0.875rem;
188 | line-height: 1.25rem;
189 | }
190 |
191 | p {
192 | display: block;
193 | width: fit-content;
194 | }
195 |
196 | span {
197 | font-weight: inherit;
198 | font-size: inherit;
199 | line-height: inherit;
200 | }
201 |
202 | /* Content Sectioning */
203 |
204 | main {}
205 |
206 | header {}
207 |
208 | footer {}
209 |
210 | nav {}
211 |
212 | section {}
213 |
214 | address {
215 | font-style: normal;
216 | }
217 |
218 | aside {
219 | float: right;
220 | width: 40%;
221 | padding: 0.75rem;
222 | margin: 0.5rem;
223 | font-style: italic;
224 | color: var(--color-primary);
225 | background-color: var(--background-muted);
226 | border-radius: var(--border-radius-large);
227 | }
228 |
229 |
230 | /* Text Content */
231 | blockquote {
232 | position: relative;
233 | padding-left: 1.5rem;
234 | margin: 0;
235 | }
236 |
237 | blockquote::after {
238 | content: "";
239 | display: block;
240 | position: absolute;
241 | left: 0;
242 | top: 0;
243 | height: 100%;
244 | border-left: 7px solid var(--border-primary);
245 | border-radius: 6px;
246 | }
247 |
248 | blockquote footer {
249 | padding-top: 1rem;
250 | }
251 |
252 | dd {
253 | padding-bottom: 11px;
254 | }
255 |
256 | dl {}
257 |
258 | dt {
259 | font-weight: bold;
260 | }
261 |
262 | figure {}
263 |
264 | figcaption {
265 | padding-top: 10px;
266 | font-size: 0.8rem;
267 | }
268 |
269 | hr {}
270 |
271 |
272 | ul,
273 | ol {
274 | list-style-position: inside;
275 | padding-left: 1rem;
276 | }
277 |
278 | li {
279 | line-height: 1.6em;
280 | }
281 |
282 | /* Inline Text Elements */
283 |
284 | a {
285 | color: var(--color-link);
286 | }
287 |
288 | a:active,
289 | a:focus,
290 | a:hover {
291 | text-decoration: none;
292 | }
293 |
294 | .link-button {
295 | border: none;
296 | color: var(--color-primary);
297 | background: var(--background-primary);
298 | box-shadow: none;
299 | padding: 0.5rem 1rem;
300 | }
301 |
302 | .link-button:hover {
303 | box-shadow: none;
304 | transform: unset;
305 | text-decoration: underline;
306 | }
307 |
308 | mark,
309 | samp,
310 | kbd,
311 | code,
312 | time {
313 | border-radius: var(--highlight-border-radius, 4px);
314 | box-decoration-break: clone;
315 | -webkit-box-decoration-break: clone;
316 | }
317 |
318 | mark {
319 | background-color: var(--color-highlight);
320 | padding: 0 4px;
321 | }
322 |
323 | samp {
324 | font-weight: bold;
325 | padding: 0.5rem 1rem;
326 | background-color: var(--background-muted);
327 | color: var(--color-primary);
328 | }
329 |
330 | kbd,
331 | time {
332 | padding: 0rem 0.5rem;
333 | background-color: var(--background-muted);
334 | color: var(--color-primary);
335 | }
336 |
337 | code,
338 | pre {
339 | font-size: 0.9em;
340 | padding: 0.2rem 0.5rem;
341 | background: var(--background-muted);
342 | border: 1px solid var(--border-primary);
343 | max-width: fit-content;
344 | overflow-x: auto;
345 | }
346 |
347 | pre>code {
348 | padding: 10px;
349 | border: 0;
350 | display: block;
351 | overflow-x: auto;
352 | }
353 |
354 | pre {
355 | border-radius: var(--border-radius-large);
356 | }
357 |
358 | sup,
359 | sub {
360 | line-height: normal;
361 | }
362 |
363 | /* Image and multimedia */
364 | audio {
365 | width: 100%;
366 | }
367 |
368 | audio,
369 | img,
370 | video {
371 | border-radius: var(--border-radius-large);
372 | max-width: 100%;
373 | }
374 |
375 | img {
376 | height: auto;
377 | }
378 |
379 | /* Tables */
380 | table {
381 | width: fit-content;
382 | border: 1px solid var(--border-primary);
383 | background: var(--background-muted);
384 | border-radius: var(--border-radius-small);
385 | }
386 |
387 | table tr:last-child td:first-child {
388 | border-bottom-left-radius: 8px;
389 | }
390 |
391 | table tr:last-child td:last-child {
392 | border-bottom-right-radius: 8px;
393 | }
394 |
395 | table tr:first-child th:first-child {
396 | border-top-left-radius: 8px;
397 | }
398 |
399 | table tr:first-child th:last-child {
400 | border-top-right-radius: 8px;
401 | }
402 |
403 | th {
404 | background-color: var(--background-muted);
405 | }
406 |
407 | td {
408 | background: var(--background-primary);
409 | }
410 |
411 | td,
412 | th {
413 | text-align: left;
414 | padding: 8px;
415 | }
416 |
417 | thead {
418 | border-collapse: collapse;
419 | }
420 |
421 | tfoot {
422 | border-top: 1px solid black;
423 | }
424 |
425 | table tr:hover td,
426 | tbody tr:nth-child(even):hover td {
427 | background-color: var(--background-muted);
428 | }
429 |
430 | /* Form elements */
431 | input,
432 | button,
433 | select,
434 | optgroup,
435 | textarea {
436 | /* margin: 0; */
437 | }
438 |
439 | button,
440 | select,
441 | input[type="submit"],
442 | input[type="button"],
443 | input[type="checkbox"],
444 | input[type="range"],
445 | input[type="radio"] {
446 | cursor: pointer;
447 | }
448 |
449 | button {
450 | color: var(--color-primary);
451 | background-color: var(--background-secondary);
452 | width: fit-content;
453 | font-family: inherit;
454 | font-size: inherit;
455 | font-weight: 500;
456 | padding: 0.25rem 1.25rem;
457 | border: 1.5px solid var(--border-secondary);
458 | border-radius: var(--border-radius-small);
459 | box-shadow: 2px 2px 0 0 var(--border-secondary);
460 | transition-duration: 200ms;
461 | }
462 |
463 | button:hover {
464 | box-shadow: 0 0 0 0 var(--border-secondary);
465 | transform: translate(2px, 2px);
466 | }
467 |
468 | button[disabled]:hover {
469 | box-shadow: 2px 2px 0 0 var(--border-secondary);
470 | transform: translate(0, 0);
471 | }
472 |
473 | button:disabled,
474 | button[disabled] {
475 | border: 1.5px solid var(--border-secondary);
476 | cursor: initial;
477 | opacity: 0.55;
478 | }
479 |
480 | label {
481 | display: block;
482 | max-width: fit-content;
483 | font-weight: 500;
484 | }
485 |
486 | input,
487 | textarea,
488 | select {
489 | font-size: 1em;
490 | background-color: var(--background-muted);
491 | border: 1px solid var(--border-secondary);
492 | color: var(--color-primary);
493 | padding: 0.5rem 0.75rem;
494 | border-radius: var(--border-radius-small);
495 | width: 100%;
496 | /* max-width: fit-content; */
497 | outline: none;
498 | appearance: none;
499 | }
500 |
501 | input:disabled {
502 | cursor: not-allowed;
503 | opacity: 0.6;
504 | }
505 |
506 | input[type="number"].buttonless {
507 | appearance: textfield;
508 | -moz-appearance: textfield;
509 | }
510 |
511 | input[type="number"].buttonless::-webkit-inner-spin-button {
512 | display: none;
513 | appearance: none;
514 | }
515 |
516 | input[type="number"].buttonless::-webkit-outer-spin-button {
517 | -webkit-appearance: none;
518 | margin: 0;
519 | }
520 |
521 | input[type="checkbox"],
522 | input[type="radio"] {
523 | padding: 0.5rem;
524 | width: fit-content;
525 | vertical-align: middle;
526 | position: relative;
527 | margin: 0.5rem 0.25rem 0.5rem 0.5rem;
528 | /* margin-right: 0.33em;
529 | margin-top: 0.31em; */
530 | }
531 |
532 | input[type="checkbox"] {
533 | border-radius: 7px;
534 | margin-left: 0;
535 | }
536 |
537 | input[type="radio"] {
538 | border-radius: 100%;
539 | }
540 |
541 | input[type="checkbox"]:checked,
542 | input[type="radio"]:checked {
543 | background: var(--border-secondary);
544 | }
545 |
546 | input[type="range"] {
547 | vertical-align: middle;
548 | padding: 0;
549 | }
550 |
551 | input[type="color"] {
552 | appearance: none;
553 | border: none;
554 | outline-style: none;
555 | padding: initial;
556 | max-width: initial;
557 | height: 2rem;
558 | width: 3rem;
559 | }
560 |
561 | select:is([multiple]) {
562 | background: none;
563 | height: fit-content;
564 | }
565 |
566 | fieldset:focus-within,
567 | input:focus-within,
568 | textarea:focus-within,
569 | select:focus-within {
570 | border-color: var(--border-highlight);
571 | }
572 |
573 | fieldset:hover,
574 | input:hover,
575 | textarea:hover,
576 | select:hover {
577 | border-color: var(--border-highlight);
578 | }
579 |
580 | progress {
581 | appearance: none;
582 | height: 1rem;
583 | margin: 0.75rem 0;
584 | }
585 |
586 | progress::-webkit-progress-bar {
587 | background: var(--background-main);
588 | border: 1px solid var(--border-primary);
589 | border-radius: var(--highlight-border-radius);
590 | }
591 |
592 | progress::-webkit-progress-value {
593 | background-color: var(--color-link);
594 | border-radius: var(--border-radius-small);
595 | }
596 |
597 | progress::-moz-progress-bar {
598 | background-color: var(--color-link);
599 | border-radius: var(--border-radius-small);
600 | }
601 |
602 | fieldset {
603 | border: 1px solid var(--border-primary);
604 | border-radius: var(--border-radius-small);
605 | margin: 0;
606 | margin-bottom: 6px;
607 | padding: 1rem;
608 | max-width: fit-content;
609 | }
610 |
611 | /* Interactive elements */
612 | details {
613 | border: 1px solid var(--border-primary);
614 | border-radius: var(--border-radius-small);
615 | padding: 0.5rem 0.75rem;
616 | }
617 |
618 | summary {
619 | font-weight: bold;
620 | }
621 |
622 | details[open] summary {
623 | border-bottom: 1px solid var(--border-primary);
624 | margin-bottom: 0.5rem;
625 | }
626 |
627 | .card,
628 | .button-card {
629 | background-color: var(--background-muted);
630 | color: var(--color-primary);
631 | padding: 1rem;
632 | border-radius: 0.5rem;
633 | }
634 |
635 | .card {
636 | border: 1px solid var(--color-link-transparent);
637 | }
638 |
639 | .button-card {
640 | border: 1px solid var(--border-secondary);
641 | }
642 |
643 | .button-card {
644 | box-shadow: none;
645 | }
646 |
647 | .button-card:hover {
648 | transform: translate(0);
649 | box-shadow: none;
650 | border-color: var(--border-highlight);
651 | }
652 |
653 | .card-button-primary,
654 | .card-button-secondary {
655 | border: none !important;
656 | box-shadow: none !important;
657 | }
658 |
659 | .card-button-primary {
660 | color: var(--background-primary);
661 | background: var(--color-link);
662 | }
663 |
664 | .card-button-secondary {
665 | color: var(--color-link);
666 | background: var(--color-link-transparent);
667 | }
668 |
669 | .card-button-primary:hover,
670 | .card-button-secondary:hover {
671 | transform: none;
672 | opacity: 0.6;
673 | }
674 |
675 |
676 | @media (min-width: 640px) {
677 | body {
678 | font-size: 1rem;
679 | line-height: 1.5rem;
680 | }
681 |
682 | /* h1, */
683 | .text-large {
684 | font-size: 2.25rem;
685 | line-height: 2.5rem;
686 | }
687 |
688 | h1 {
689 | font-size: 1.875rem;
690 | line-height: 2.25rem;
691 | }
692 |
693 | h2 {
694 | font-size: 1.5rem;
695 | line-height: 2rem;
696 | }
697 |
698 | h3,
699 | .text-medium {
700 | font-size: 1.25rem;
701 | line-height: 1.75rem;
702 | }
703 |
704 | h4 {
705 | font-size: 1.125rem;
706 | line-height: 1.75rem;
707 | }
708 |
709 | h5,
710 | h6 {
711 | font-size: 1rem;
712 | line-height: 1.5rem;
713 | }
714 |
715 | button {
716 | width: fit-content;
717 | }
718 |
719 | input {
720 | max-width: 600px;
721 | }
722 | }
--------------------------------------------------------------------------------
/svelte-tutorial/src/fanta.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Fanta.css (Fantasy + CSS)
3 | * Version 0.1.0
4 | * https://github.com/jamezmca/fantacss
5 | *
6 | * Sections
7 | * 1. Content sectioning
8 | * 2. Text content
9 | * 3. Inline text semantics
10 | * 4. Image and multimedia
11 | * 5. Tables
12 | * 6. Forms
13 | * 7. Interactive elements
14 | *
15 | */
16 |
17 | /* @import url('https://fonts.googleapis.com/css2?family=Eczar:wght@400..800&family=Grenze:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); */
18 | @import url('https://fonts.googleapis.com/css2?family=Eczar:wght@400..800&family=Press+Start+2P&display=swap');
19 |
20 | /* :root {
21 | --background-primary: white;
22 | --background-secondary: '';
23 | --background-tertiary: #dbeafe;
24 | --background-accent: '';
25 | --background-compliment: '';
26 | --background-gradient: '';
27 | --background-muted: #f8fafc;
28 |
29 | --color-primary: #030615;
30 | --color-secondary: '';
31 | --color-tertiary: '';
32 | --color-accent: '';
33 | --color-compliment: '';
34 | --color-gradient: '';
35 | --color-muted: ;
36 | --color-link: #2563eb;
37 | --color-link-transparent: rgba(37, 99, 235, 0.1);
38 |
39 | --color-success: '';
40 | --color-warning: '';
41 | --color-error: '';
42 | --color-info: '';
43 | --color-highlight: #fef9c3;
44 |
45 | --gradient-start: #9580ff;
46 | --gradient-end: #80ffea;
47 |
48 | --border-primary: #f1f5f9;
49 | --border-secondary: #bed1e7;
50 | --border-highlight: #64748b;
51 | --border-tertiary: '';
52 |
53 | --shadow-dark: '';
54 | --shadow-light: '';
55 | --shadow-text: '';
56 |
57 | --padding-small: 1rem;
58 | --padding-large: 2rem;
59 |
60 | --border-radius-small: 0.5rem;
61 | --border-radius-large: 0.75rem;
62 | --highlight-border-radius: 0.5rem;
63 |
64 | --text-selection: '';
65 | } */
66 |
67 | /* @media (prefers-color-scheme: dark) { */
68 | :root {
69 | --background-primary: #030615;
70 | --background-secondary: rgb(23 37 84);
71 | --background-tertiary: #121424;
72 | --background-accent: '';
73 | --background-compliment: '';
74 | --background-gradient: '';
75 | --background-muted: #1a1e32;
76 |
77 | --color-primary: rgb(219 234 254);
78 | --color-secondary: '';
79 | --color-tertiary: '';
80 | --color-accent: '';
81 | --color-compliment: '';
82 | --color-gradient: '';
83 | --color-muted: ;
84 | --color-link: #60a5fa;
85 | --color-link-transparent: rgba(37, 99, 235, 0.1);
86 |
87 | --color-success: '';
88 | --color-warning: '';
89 | --color-error: '';
90 | --color-info: '';
91 | --color-highlight: #fef9c3;
92 |
93 | --gradient-start: #0084ff;
94 | --gradient-end: #00fffb;
95 |
96 | --border-primary: #29325b;
97 | --border-secondary: #93c5fd;
98 | --border-highlight: #4649af;
99 | --border-tertiary: '';
100 |
101 | --shadow-dark: '';
102 | --shadow-light: '';
103 | --shadow-text: '';
104 |
105 | --padding-small: 1rem;
106 | --padding-large: 2rem;
107 |
108 | --border-radius-small: 0.5rem;
109 | --border-radius-large: 0.75rem;
110 | --highlight-border-radius: 0.5rem;
111 |
112 | --text-selection: '';
113 | }
114 |
115 | /* } */
116 |
117 | * {
118 | margin: 0;
119 | padding: 0;
120 | box-sizing: border-box;
121 | font-family: "Eczar", serif;
122 | }
123 |
124 | body {
125 | background: var(--background-primary);
126 | color: var(--color-primary);
127 | font-size: 0.875rem;
128 | line-height: 1.6rem;
129 | }
130 |
131 | /* Special */
132 | .text-gradient {
133 | -webkit-text-fill-color: transparent;
134 | -webkit-background-clip: text;
135 | background-image: linear-gradient(180deg, var(--gradient-start) 0, var(--gradient-end) 100%);
136 | background-size: 100%;
137 | -webkit-box-decoration-break: clone;
138 | }
139 |
140 |
141 | /* Typography */
142 | h1,
143 | h2,
144 | h3,
145 | h4,
146 | h5,
147 | h6,
148 | button {
149 | font-family: "Press Start 2P", serif;
150 | width: fit-content;
151 | }
152 |
153 | /* h1, */
154 | .text-large {
155 | font-size: 1.875rem;
156 | line-height: 2.25rem;
157 | }
158 |
159 | h1 {
160 | font-size: 1.5rem;
161 | line-height: 2rem;
162 | }
163 |
164 | .special-shadow {
165 | text-shadow: -1px -1px 4px #0022ffbf, 2px 2px 10px #0022ffbf, 0 0 20px #0022ffbf;
166 | color: white;
167 | }
168 |
169 | h2 {
170 | font-size: 1.25rem;
171 | line-height: 1.75rem;
172 | }
173 |
174 | h3,
175 | .text-medium {
176 | font-size: 1.125rem;
177 | line-height: 1.75rem;
178 | }
179 |
180 | h4 {
181 | font-size: 1rem;
182 | line-height: 1.5rem;
183 | }
184 |
185 | h5,
186 | h6 {
187 | font-size: 0.875rem;
188 | line-height: 1.25rem;
189 | }
190 |
191 | p {
192 | display: block;
193 | width: fit-content;
194 | }
195 |
196 | span {
197 | font-weight: inherit;
198 | font-size: inherit;
199 | line-height: inherit;
200 | }
201 |
202 | /* Content Sectioning */
203 |
204 | main {}
205 |
206 | header {}
207 |
208 | footer {}
209 |
210 | nav {}
211 |
212 | section {}
213 |
214 | address {
215 | font-style: normal;
216 | }
217 |
218 | aside {
219 | float: right;
220 | width: 40%;
221 | padding: 0.75rem;
222 | margin: 0.5rem;
223 | font-style: italic;
224 | color: var(--color-primary);
225 | background-color: var(--background-muted);
226 | border-radius: var(--border-radius-large);
227 | }
228 |
229 |
230 | /* Text Content */
231 | blockquote {
232 | position: relative;
233 | padding-left: 1.5rem;
234 | margin: 0;
235 | }
236 |
237 | blockquote::after {
238 | content: "";
239 | display: block;
240 | position: absolute;
241 | left: 0;
242 | top: 0;
243 | height: 100%;
244 | border-left: 7px solid var(--border-primary);
245 | border-radius: 6px;
246 | }
247 |
248 | blockquote footer {
249 | padding-top: 1rem;
250 | }
251 |
252 | dd {
253 | padding-bottom: 11px;
254 | }
255 |
256 | dl {}
257 |
258 | dt {
259 | font-weight: bold;
260 | }
261 |
262 | figure {}
263 |
264 | figcaption {
265 | padding-top: 10px;
266 | font-size: 0.8rem;
267 | }
268 |
269 | hr {}
270 |
271 |
272 | ul,
273 | ol {
274 | list-style-position: inside;
275 | padding-left: 1rem;
276 | }
277 |
278 | li {
279 | line-height: 1.6em;
280 | }
281 |
282 | /* Inline Text Elements */
283 |
284 | a {
285 | color: var(--color-link);
286 | }
287 |
288 | a:active,
289 | a:focus,
290 | a:hover {
291 | text-decoration: none;
292 | }
293 |
294 | .link-button {
295 | border: none;
296 | color: var(--color-primary);
297 | background: var(--background-primary);
298 | box-shadow: none;
299 | padding: 0.5rem 1rem;
300 | }
301 |
302 | .link-button:hover {
303 | box-shadow: none;
304 | transform: unset;
305 | text-decoration: underline;
306 | }
307 |
308 | mark,
309 | samp,
310 | kbd,
311 | code,
312 | time {
313 | border-radius: var(--highlight-border-radius, 4px);
314 | box-decoration-break: clone;
315 | -webkit-box-decoration-break: clone;
316 | }
317 |
318 | mark {
319 | background-color: var(--color-highlight);
320 | padding: 0 4px;
321 | }
322 |
323 | samp {
324 | font-weight: bold;
325 | padding: 0.5rem 1rem;
326 | background-color: var(--background-muted);
327 | color: var(--color-primary);
328 | }
329 |
330 | kbd,
331 | time {
332 | padding: 0rem 0.5rem;
333 | background-color: var(--background-muted);
334 | color: var(--color-primary);
335 | }
336 |
337 | code,
338 | pre {
339 | font-size: 0.9em;
340 | padding: 0.2rem 0.5rem;
341 | background: var(--background-muted);
342 | border: 1px solid var(--border-primary);
343 | max-width: fit-content;
344 | overflow-x: auto;
345 | }
346 |
347 | pre>code {
348 | padding: 10px;
349 | border: 0;
350 | display: block;
351 | overflow-x: auto;
352 | }
353 |
354 | pre {
355 | border-radius: var(--border-radius-large);
356 | }
357 |
358 | sup,
359 | sub {
360 | line-height: normal;
361 | }
362 |
363 | /* Image and multimedia */
364 | audio {
365 | width: 100%;
366 | }
367 |
368 | audio,
369 | img,
370 | video {
371 | border-radius: var(--border-radius-large);
372 | max-width: 100%;
373 | }
374 |
375 | img {
376 | height: auto;
377 | }
378 |
379 | /* Tables */
380 | table {
381 | width: fit-content;
382 | border: 1px solid var(--border-primary);
383 | background: var(--background-muted);
384 | border-radius: var(--border-radius-small);
385 | }
386 |
387 | table tr:last-child td:first-child {
388 | border-bottom-left-radius: 8px;
389 | }
390 |
391 | table tr:last-child td:last-child {
392 | border-bottom-right-radius: 8px;
393 | }
394 |
395 | table tr:first-child th:first-child {
396 | border-top-left-radius: 8px;
397 | }
398 |
399 | table tr:first-child th:last-child {
400 | border-top-right-radius: 8px;
401 | }
402 |
403 | th {
404 | background-color: var(--background-muted);
405 | }
406 |
407 | td {
408 | background: var(--background-primary);
409 | }
410 |
411 | td,
412 | th {
413 | text-align: left;
414 | padding: 8px;
415 | }
416 |
417 | thead {
418 | border-collapse: collapse;
419 | }
420 |
421 | tfoot {
422 | border-top: 1px solid black;
423 | }
424 |
425 | table tr:hover td,
426 | tbody tr:nth-child(even):hover td {
427 | background-color: var(--background-muted);
428 | }
429 |
430 | /* Form elements */
431 | input,
432 | button,
433 | select,
434 | optgroup,
435 | textarea {
436 | /* margin: 0; */
437 | }
438 |
439 | button,
440 | select,
441 | input[type="submit"],
442 | input[type="button"],
443 | input[type="checkbox"],
444 | input[type="range"],
445 | input[type="radio"] {
446 | cursor: pointer;
447 | }
448 |
449 | button {
450 | color: var(--color-primary);
451 | background-color: var(--background-secondary);
452 | width: fit-content;
453 | font-family: inherit;
454 | font-size: inherit;
455 | font-weight: 500;
456 | padding: 0.25rem 1.25rem;
457 | border: 1.5px solid var(--border-secondary);
458 | border-radius: var(--border-radius-small);
459 | box-shadow: 2px 2px 0 0 var(--border-secondary);
460 | transition-duration: 200ms;
461 | }
462 |
463 | button:hover {
464 | box-shadow: 0 0 0 0 var(--border-secondary);
465 | transform: translate(2px, 2px);
466 | }
467 |
468 | button[disabled]:hover {
469 | box-shadow: 2px 2px 0 0 var(--border-secondary);
470 | transform: translate(0, 0);
471 | }
472 |
473 | button:disabled,
474 | button[disabled] {
475 | border: 1.5px solid var(--border-secondary);
476 | cursor: initial;
477 | opacity: 0.55;
478 | }
479 |
480 | label {
481 | display: block;
482 | max-width: fit-content;
483 | font-weight: 500;
484 | }
485 |
486 | input,
487 | textarea,
488 | select {
489 | font-size: 1em;
490 | background-color: var(--background-muted);
491 | border: 1px solid var(--border-secondary);
492 | color: var(--color-primary);
493 | padding: 0.5rem 0.75rem;
494 | border-radius: var(--border-radius-small);
495 | width: 100%;
496 | /* max-width: fit-content; */
497 | outline: none;
498 | appearance: none;
499 | }
500 |
501 | input:disabled {
502 | cursor: not-allowed;
503 | opacity: 0.6;
504 | }
505 |
506 | input[type="number"].buttonless {
507 | appearance: textfield;
508 | -moz-appearance: textfield;
509 | }
510 |
511 | input[type="number"].buttonless::-webkit-inner-spin-button {
512 | display: none;
513 | appearance: none;
514 | }
515 |
516 | input[type="number"].buttonless::-webkit-outer-spin-button {
517 | -webkit-appearance: none;
518 | margin: 0;
519 | }
520 |
521 | input[type="checkbox"],
522 | input[type="radio"] {
523 | padding: 0.5rem;
524 | width: fit-content;
525 | vertical-align: middle;
526 | position: relative;
527 | margin: 0.5rem 0.25rem 0.5rem 0.5rem;
528 | /* margin-right: 0.33em;
529 | margin-top: 0.31em; */
530 | }
531 |
532 | input[type="checkbox"] {
533 | border-radius: 7px;
534 | margin-left: 0;
535 | }
536 |
537 | input[type="radio"] {
538 | border-radius: 100%;
539 | }
540 |
541 | input[type="checkbox"]:checked,
542 | input[type="radio"]:checked {
543 | background: var(--border-secondary);
544 | }
545 |
546 | input[type="range"] {
547 | vertical-align: middle;
548 | padding: 0;
549 | }
550 |
551 | input[type="color"] {
552 | appearance: none;
553 | border: none;
554 | outline-style: none;
555 | padding: initial;
556 | max-width: initial;
557 | height: 2rem;
558 | width: 3rem;
559 | }
560 |
561 | select:is([multiple]) {
562 | background: none;
563 | height: fit-content;
564 | }
565 |
566 | fieldset:focus-within,
567 | input:focus-within,
568 | textarea:focus-within,
569 | select:focus-within {
570 | border-color: var(--border-highlight);
571 | }
572 |
573 | fieldset:hover,
574 | input:hover,
575 | textarea:hover,
576 | select:hover {
577 | border-color: var(--border-highlight);
578 | }
579 |
580 | progress {
581 | appearance: none;
582 | height: 1rem;
583 | margin: 0.75rem 0;
584 | }
585 |
586 | progress::-webkit-progress-bar {
587 | background: var(--background-main);
588 | border: 1px solid var(--border-primary);
589 | border-radius: var(--highlight-border-radius);
590 | }
591 |
592 | progress::-webkit-progress-value {
593 | background-color: var(--color-link);
594 | border-radius: var(--border-radius-small);
595 | }
596 |
597 | progress::-moz-progress-bar {
598 | background-color: var(--color-link);
599 | border-radius: var(--border-radius-small);
600 | }
601 |
602 | fieldset {
603 | border: 1px solid var(--border-primary);
604 | border-radius: var(--border-radius-small);
605 | margin: 0;
606 | margin-bottom: 6px;
607 | padding: 1rem;
608 | max-width: fit-content;
609 | }
610 |
611 | /* Interactive elements */
612 | details {
613 | border: 1px solid var(--border-primary);
614 | border-radius: var(--border-radius-small);
615 | padding: 0.5rem 0.75rem;
616 | }
617 |
618 | summary {
619 | font-weight: bold;
620 | }
621 |
622 | details[open] summary {
623 | border-bottom: 1px solid var(--border-primary);
624 | margin-bottom: 0.5rem;
625 | }
626 |
627 | .card,
628 | .button-card {
629 | background-color: var(--background-muted);
630 | color: var(--color-primary);
631 | padding: 1rem;
632 | border-radius: 0.5rem;
633 | }
634 |
635 | .card {
636 | border: 1px solid var(--color-link-transparent);
637 | }
638 |
639 | .button-card {
640 | border: 1px solid var(--border-secondary);
641 | }
642 |
643 | .button-card {
644 | box-shadow: none;
645 | }
646 |
647 | .button-card:hover {
648 | transform: translate(0);
649 | box-shadow: none;
650 | border-color: var(--border-highlight);
651 | }
652 |
653 | .card-button-primary,
654 | .card-button-secondary {
655 | border: none !important;
656 | box-shadow: none !important;
657 | }
658 |
659 | .card-button-primary {
660 | color: var(--background-primary);
661 | background: var(--color-link);
662 | }
663 |
664 | .card-button-secondary {
665 | color: var(--color-link);
666 | background: var(--color-link-transparent);
667 | }
668 |
669 | .card-button-primary:hover,
670 | .card-button-secondary:hover {
671 | transform: none;
672 | opacity: 0.6;
673 | }
674 |
675 |
676 | @media (min-width: 640px) {
677 | body {
678 | font-size: 1rem;
679 | line-height: 1.5rem;
680 | }
681 |
682 | /* h1, */
683 | .text-large {
684 | font-size: 2.25rem;
685 | line-height: 2.5rem;
686 | }
687 |
688 | h1 {
689 | font-size: 1.875rem;
690 | line-height: 2.25rem;
691 | }
692 |
693 | h2 {
694 | font-size: 1.5rem;
695 | line-height: 2rem;
696 | }
697 |
698 | h3,
699 | .text-medium {
700 | font-size: 1.25rem;
701 | line-height: 1.75rem;
702 | }
703 |
704 | h4 {
705 | font-size: 1.125rem;
706 | line-height: 1.75rem;
707 | }
708 |
709 | h5,
710 | h6 {
711 | font-size: 1rem;
712 | line-height: 1.5rem;
713 | }
714 |
715 | button {
716 | width: fit-content;
717 | }
718 |
719 | input {
720 | max-width: 600px;
721 | }
722 | }
--------------------------------------------------------------------------------
/vue-tutorial/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-tutorial",
3 | "version": "0.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "vue-tutorial",
9 | "version": "0.0.0",
10 | "dependencies": {
11 | "vue": "^3.5.13"
12 | },
13 | "devDependencies": {
14 | "@vitejs/plugin-vue": "^5.2.1",
15 | "vite": "^6.2.0"
16 | }
17 | },
18 | "node_modules/@babel/helper-string-parser": {
19 | "version": "7.25.9",
20 | "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
21 | "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
22 | "engines": {
23 | "node": ">=6.9.0"
24 | }
25 | },
26 | "node_modules/@babel/helper-validator-identifier": {
27 | "version": "7.25.9",
28 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
29 | "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
30 | "engines": {
31 | "node": ">=6.9.0"
32 | }
33 | },
34 | "node_modules/@babel/parser": {
35 | "version": "7.27.0",
36 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
37 | "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
38 | "dependencies": {
39 | "@babel/types": "^7.27.0"
40 | },
41 | "bin": {
42 | "parser": "bin/babel-parser.js"
43 | },
44 | "engines": {
45 | "node": ">=6.0.0"
46 | }
47 | },
48 | "node_modules/@babel/types": {
49 | "version": "7.27.0",
50 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
51 | "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
52 | "dependencies": {
53 | "@babel/helper-string-parser": "^7.25.9",
54 | "@babel/helper-validator-identifier": "^7.25.9"
55 | },
56 | "engines": {
57 | "node": ">=6.9.0"
58 | }
59 | },
60 | "node_modules/@esbuild/aix-ppc64": {
61 | "version": "0.25.2",
62 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
63 | "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
64 | "cpu": [
65 | "ppc64"
66 | ],
67 | "dev": true,
68 | "optional": true,
69 | "os": [
70 | "aix"
71 | ],
72 | "engines": {
73 | "node": ">=18"
74 | }
75 | },
76 | "node_modules/@esbuild/android-arm": {
77 | "version": "0.25.2",
78 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
79 | "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
80 | "cpu": [
81 | "arm"
82 | ],
83 | "dev": true,
84 | "optional": true,
85 | "os": [
86 | "android"
87 | ],
88 | "engines": {
89 | "node": ">=18"
90 | }
91 | },
92 | "node_modules/@esbuild/android-arm64": {
93 | "version": "0.25.2",
94 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
95 | "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
96 | "cpu": [
97 | "arm64"
98 | ],
99 | "dev": true,
100 | "optional": true,
101 | "os": [
102 | "android"
103 | ],
104 | "engines": {
105 | "node": ">=18"
106 | }
107 | },
108 | "node_modules/@esbuild/android-x64": {
109 | "version": "0.25.2",
110 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
111 | "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
112 | "cpu": [
113 | "x64"
114 | ],
115 | "dev": true,
116 | "optional": true,
117 | "os": [
118 | "android"
119 | ],
120 | "engines": {
121 | "node": ">=18"
122 | }
123 | },
124 | "node_modules/@esbuild/darwin-arm64": {
125 | "version": "0.25.2",
126 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
127 | "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
128 | "cpu": [
129 | "arm64"
130 | ],
131 | "dev": true,
132 | "optional": true,
133 | "os": [
134 | "darwin"
135 | ],
136 | "engines": {
137 | "node": ">=18"
138 | }
139 | },
140 | "node_modules/@esbuild/darwin-x64": {
141 | "version": "0.25.2",
142 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
143 | "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
144 | "cpu": [
145 | "x64"
146 | ],
147 | "dev": true,
148 | "optional": true,
149 | "os": [
150 | "darwin"
151 | ],
152 | "engines": {
153 | "node": ">=18"
154 | }
155 | },
156 | "node_modules/@esbuild/freebsd-arm64": {
157 | "version": "0.25.2",
158 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
159 | "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
160 | "cpu": [
161 | "arm64"
162 | ],
163 | "dev": true,
164 | "optional": true,
165 | "os": [
166 | "freebsd"
167 | ],
168 | "engines": {
169 | "node": ">=18"
170 | }
171 | },
172 | "node_modules/@esbuild/freebsd-x64": {
173 | "version": "0.25.2",
174 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
175 | "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
176 | "cpu": [
177 | "x64"
178 | ],
179 | "dev": true,
180 | "optional": true,
181 | "os": [
182 | "freebsd"
183 | ],
184 | "engines": {
185 | "node": ">=18"
186 | }
187 | },
188 | "node_modules/@esbuild/linux-arm": {
189 | "version": "0.25.2",
190 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
191 | "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
192 | "cpu": [
193 | "arm"
194 | ],
195 | "dev": true,
196 | "optional": true,
197 | "os": [
198 | "linux"
199 | ],
200 | "engines": {
201 | "node": ">=18"
202 | }
203 | },
204 | "node_modules/@esbuild/linux-arm64": {
205 | "version": "0.25.2",
206 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
207 | "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
208 | "cpu": [
209 | "arm64"
210 | ],
211 | "dev": true,
212 | "optional": true,
213 | "os": [
214 | "linux"
215 | ],
216 | "engines": {
217 | "node": ">=18"
218 | }
219 | },
220 | "node_modules/@esbuild/linux-ia32": {
221 | "version": "0.25.2",
222 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
223 | "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
224 | "cpu": [
225 | "ia32"
226 | ],
227 | "dev": true,
228 | "optional": true,
229 | "os": [
230 | "linux"
231 | ],
232 | "engines": {
233 | "node": ">=18"
234 | }
235 | },
236 | "node_modules/@esbuild/linux-loong64": {
237 | "version": "0.25.2",
238 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
239 | "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
240 | "cpu": [
241 | "loong64"
242 | ],
243 | "dev": true,
244 | "optional": true,
245 | "os": [
246 | "linux"
247 | ],
248 | "engines": {
249 | "node": ">=18"
250 | }
251 | },
252 | "node_modules/@esbuild/linux-mips64el": {
253 | "version": "0.25.2",
254 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
255 | "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
256 | "cpu": [
257 | "mips64el"
258 | ],
259 | "dev": true,
260 | "optional": true,
261 | "os": [
262 | "linux"
263 | ],
264 | "engines": {
265 | "node": ">=18"
266 | }
267 | },
268 | "node_modules/@esbuild/linux-ppc64": {
269 | "version": "0.25.2",
270 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
271 | "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
272 | "cpu": [
273 | "ppc64"
274 | ],
275 | "dev": true,
276 | "optional": true,
277 | "os": [
278 | "linux"
279 | ],
280 | "engines": {
281 | "node": ">=18"
282 | }
283 | },
284 | "node_modules/@esbuild/linux-riscv64": {
285 | "version": "0.25.2",
286 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
287 | "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
288 | "cpu": [
289 | "riscv64"
290 | ],
291 | "dev": true,
292 | "optional": true,
293 | "os": [
294 | "linux"
295 | ],
296 | "engines": {
297 | "node": ">=18"
298 | }
299 | },
300 | "node_modules/@esbuild/linux-s390x": {
301 | "version": "0.25.2",
302 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
303 | "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
304 | "cpu": [
305 | "s390x"
306 | ],
307 | "dev": true,
308 | "optional": true,
309 | "os": [
310 | "linux"
311 | ],
312 | "engines": {
313 | "node": ">=18"
314 | }
315 | },
316 | "node_modules/@esbuild/linux-x64": {
317 | "version": "0.25.2",
318 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
319 | "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
320 | "cpu": [
321 | "x64"
322 | ],
323 | "dev": true,
324 | "optional": true,
325 | "os": [
326 | "linux"
327 | ],
328 | "engines": {
329 | "node": ">=18"
330 | }
331 | },
332 | "node_modules/@esbuild/netbsd-arm64": {
333 | "version": "0.25.2",
334 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
335 | "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
336 | "cpu": [
337 | "arm64"
338 | ],
339 | "dev": true,
340 | "optional": true,
341 | "os": [
342 | "netbsd"
343 | ],
344 | "engines": {
345 | "node": ">=18"
346 | }
347 | },
348 | "node_modules/@esbuild/netbsd-x64": {
349 | "version": "0.25.2",
350 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
351 | "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
352 | "cpu": [
353 | "x64"
354 | ],
355 | "dev": true,
356 | "optional": true,
357 | "os": [
358 | "netbsd"
359 | ],
360 | "engines": {
361 | "node": ">=18"
362 | }
363 | },
364 | "node_modules/@esbuild/openbsd-arm64": {
365 | "version": "0.25.2",
366 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
367 | "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
368 | "cpu": [
369 | "arm64"
370 | ],
371 | "dev": true,
372 | "optional": true,
373 | "os": [
374 | "openbsd"
375 | ],
376 | "engines": {
377 | "node": ">=18"
378 | }
379 | },
380 | "node_modules/@esbuild/openbsd-x64": {
381 | "version": "0.25.2",
382 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
383 | "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
384 | "cpu": [
385 | "x64"
386 | ],
387 | "dev": true,
388 | "optional": true,
389 | "os": [
390 | "openbsd"
391 | ],
392 | "engines": {
393 | "node": ">=18"
394 | }
395 | },
396 | "node_modules/@esbuild/sunos-x64": {
397 | "version": "0.25.2",
398 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
399 | "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
400 | "cpu": [
401 | "x64"
402 | ],
403 | "dev": true,
404 | "optional": true,
405 | "os": [
406 | "sunos"
407 | ],
408 | "engines": {
409 | "node": ">=18"
410 | }
411 | },
412 | "node_modules/@esbuild/win32-arm64": {
413 | "version": "0.25.2",
414 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
415 | "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
416 | "cpu": [
417 | "arm64"
418 | ],
419 | "dev": true,
420 | "optional": true,
421 | "os": [
422 | "win32"
423 | ],
424 | "engines": {
425 | "node": ">=18"
426 | }
427 | },
428 | "node_modules/@esbuild/win32-ia32": {
429 | "version": "0.25.2",
430 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
431 | "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
432 | "cpu": [
433 | "ia32"
434 | ],
435 | "dev": true,
436 | "optional": true,
437 | "os": [
438 | "win32"
439 | ],
440 | "engines": {
441 | "node": ">=18"
442 | }
443 | },
444 | "node_modules/@esbuild/win32-x64": {
445 | "version": "0.25.2",
446 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
447 | "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
448 | "cpu": [
449 | "x64"
450 | ],
451 | "dev": true,
452 | "optional": true,
453 | "os": [
454 | "win32"
455 | ],
456 | "engines": {
457 | "node": ">=18"
458 | }
459 | },
460 | "node_modules/@jridgewell/sourcemap-codec": {
461 | "version": "1.5.0",
462 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
463 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
464 | },
465 | "node_modules/@rollup/rollup-android-arm-eabi": {
466 | "version": "4.39.0",
467 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz",
468 | "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==",
469 | "cpu": [
470 | "arm"
471 | ],
472 | "dev": true,
473 | "optional": true,
474 | "os": [
475 | "android"
476 | ]
477 | },
478 | "node_modules/@rollup/rollup-android-arm64": {
479 | "version": "4.39.0",
480 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz",
481 | "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==",
482 | "cpu": [
483 | "arm64"
484 | ],
485 | "dev": true,
486 | "optional": true,
487 | "os": [
488 | "android"
489 | ]
490 | },
491 | "node_modules/@rollup/rollup-darwin-arm64": {
492 | "version": "4.39.0",
493 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz",
494 | "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==",
495 | "cpu": [
496 | "arm64"
497 | ],
498 | "dev": true,
499 | "optional": true,
500 | "os": [
501 | "darwin"
502 | ]
503 | },
504 | "node_modules/@rollup/rollup-darwin-x64": {
505 | "version": "4.39.0",
506 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz",
507 | "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==",
508 | "cpu": [
509 | "x64"
510 | ],
511 | "dev": true,
512 | "optional": true,
513 | "os": [
514 | "darwin"
515 | ]
516 | },
517 | "node_modules/@rollup/rollup-freebsd-arm64": {
518 | "version": "4.39.0",
519 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz",
520 | "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==",
521 | "cpu": [
522 | "arm64"
523 | ],
524 | "dev": true,
525 | "optional": true,
526 | "os": [
527 | "freebsd"
528 | ]
529 | },
530 | "node_modules/@rollup/rollup-freebsd-x64": {
531 | "version": "4.39.0",
532 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz",
533 | "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==",
534 | "cpu": [
535 | "x64"
536 | ],
537 | "dev": true,
538 | "optional": true,
539 | "os": [
540 | "freebsd"
541 | ]
542 | },
543 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
544 | "version": "4.39.0",
545 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz",
546 | "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==",
547 | "cpu": [
548 | "arm"
549 | ],
550 | "dev": true,
551 | "optional": true,
552 | "os": [
553 | "linux"
554 | ]
555 | },
556 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
557 | "version": "4.39.0",
558 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz",
559 | "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==",
560 | "cpu": [
561 | "arm"
562 | ],
563 | "dev": true,
564 | "optional": true,
565 | "os": [
566 | "linux"
567 | ]
568 | },
569 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
570 | "version": "4.39.0",
571 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz",
572 | "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==",
573 | "cpu": [
574 | "arm64"
575 | ],
576 | "dev": true,
577 | "optional": true,
578 | "os": [
579 | "linux"
580 | ]
581 | },
582 | "node_modules/@rollup/rollup-linux-arm64-musl": {
583 | "version": "4.39.0",
584 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz",
585 | "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==",
586 | "cpu": [
587 | "arm64"
588 | ],
589 | "dev": true,
590 | "optional": true,
591 | "os": [
592 | "linux"
593 | ]
594 | },
595 | "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
596 | "version": "4.39.0",
597 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz",
598 | "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==",
599 | "cpu": [
600 | "loong64"
601 | ],
602 | "dev": true,
603 | "optional": true,
604 | "os": [
605 | "linux"
606 | ]
607 | },
608 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
609 | "version": "4.39.0",
610 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz",
611 | "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==",
612 | "cpu": [
613 | "ppc64"
614 | ],
615 | "dev": true,
616 | "optional": true,
617 | "os": [
618 | "linux"
619 | ]
620 | },
621 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
622 | "version": "4.39.0",
623 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz",
624 | "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==",
625 | "cpu": [
626 | "riscv64"
627 | ],
628 | "dev": true,
629 | "optional": true,
630 | "os": [
631 | "linux"
632 | ]
633 | },
634 | "node_modules/@rollup/rollup-linux-riscv64-musl": {
635 | "version": "4.39.0",
636 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz",
637 | "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==",
638 | "cpu": [
639 | "riscv64"
640 | ],
641 | "dev": true,
642 | "optional": true,
643 | "os": [
644 | "linux"
645 | ]
646 | },
647 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
648 | "version": "4.39.0",
649 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz",
650 | "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==",
651 | "cpu": [
652 | "s390x"
653 | ],
654 | "dev": true,
655 | "optional": true,
656 | "os": [
657 | "linux"
658 | ]
659 | },
660 | "node_modules/@rollup/rollup-linux-x64-gnu": {
661 | "version": "4.39.0",
662 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz",
663 | "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==",
664 | "cpu": [
665 | "x64"
666 | ],
667 | "dev": true,
668 | "optional": true,
669 | "os": [
670 | "linux"
671 | ]
672 | },
673 | "node_modules/@rollup/rollup-linux-x64-musl": {
674 | "version": "4.39.0",
675 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz",
676 | "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==",
677 | "cpu": [
678 | "x64"
679 | ],
680 | "dev": true,
681 | "optional": true,
682 | "os": [
683 | "linux"
684 | ]
685 | },
686 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
687 | "version": "4.39.0",
688 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz",
689 | "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==",
690 | "cpu": [
691 | "arm64"
692 | ],
693 | "dev": true,
694 | "optional": true,
695 | "os": [
696 | "win32"
697 | ]
698 | },
699 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
700 | "version": "4.39.0",
701 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz",
702 | "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==",
703 | "cpu": [
704 | "ia32"
705 | ],
706 | "dev": true,
707 | "optional": true,
708 | "os": [
709 | "win32"
710 | ]
711 | },
712 | "node_modules/@rollup/rollup-win32-x64-msvc": {
713 | "version": "4.39.0",
714 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz",
715 | "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==",
716 | "cpu": [
717 | "x64"
718 | ],
719 | "dev": true,
720 | "optional": true,
721 | "os": [
722 | "win32"
723 | ]
724 | },
725 | "node_modules/@types/estree": {
726 | "version": "1.0.7",
727 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
728 | "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
729 | "dev": true
730 | },
731 | "node_modules/@vitejs/plugin-vue": {
732 | "version": "5.2.3",
733 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz",
734 | "integrity": "sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==",
735 | "dev": true,
736 | "engines": {
737 | "node": "^18.0.0 || >=20.0.0"
738 | },
739 | "peerDependencies": {
740 | "vite": "^5.0.0 || ^6.0.0",
741 | "vue": "^3.2.25"
742 | }
743 | },
744 | "node_modules/@vue/compiler-core": {
745 | "version": "3.5.13",
746 | "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
747 | "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
748 | "dependencies": {
749 | "@babel/parser": "^7.25.3",
750 | "@vue/shared": "3.5.13",
751 | "entities": "^4.5.0",
752 | "estree-walker": "^2.0.2",
753 | "source-map-js": "^1.2.0"
754 | }
755 | },
756 | "node_modules/@vue/compiler-dom": {
757 | "version": "3.5.13",
758 | "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
759 | "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
760 | "dependencies": {
761 | "@vue/compiler-core": "3.5.13",
762 | "@vue/shared": "3.5.13"
763 | }
764 | },
765 | "node_modules/@vue/compiler-sfc": {
766 | "version": "3.5.13",
767 | "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
768 | "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
769 | "dependencies": {
770 | "@babel/parser": "^7.25.3",
771 | "@vue/compiler-core": "3.5.13",
772 | "@vue/compiler-dom": "3.5.13",
773 | "@vue/compiler-ssr": "3.5.13",
774 | "@vue/shared": "3.5.13",
775 | "estree-walker": "^2.0.2",
776 | "magic-string": "^0.30.11",
777 | "postcss": "^8.4.48",
778 | "source-map-js": "^1.2.0"
779 | }
780 | },
781 | "node_modules/@vue/compiler-ssr": {
782 | "version": "3.5.13",
783 | "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
784 | "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
785 | "dependencies": {
786 | "@vue/compiler-dom": "3.5.13",
787 | "@vue/shared": "3.5.13"
788 | }
789 | },
790 | "node_modules/@vue/reactivity": {
791 | "version": "3.5.13",
792 | "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
793 | "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
794 | "dependencies": {
795 | "@vue/shared": "3.5.13"
796 | }
797 | },
798 | "node_modules/@vue/runtime-core": {
799 | "version": "3.5.13",
800 | "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
801 | "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
802 | "dependencies": {
803 | "@vue/reactivity": "3.5.13",
804 | "@vue/shared": "3.5.13"
805 | }
806 | },
807 | "node_modules/@vue/runtime-dom": {
808 | "version": "3.5.13",
809 | "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
810 | "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
811 | "dependencies": {
812 | "@vue/reactivity": "3.5.13",
813 | "@vue/runtime-core": "3.5.13",
814 | "@vue/shared": "3.5.13",
815 | "csstype": "^3.1.3"
816 | }
817 | },
818 | "node_modules/@vue/server-renderer": {
819 | "version": "3.5.13",
820 | "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
821 | "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
822 | "dependencies": {
823 | "@vue/compiler-ssr": "3.5.13",
824 | "@vue/shared": "3.5.13"
825 | },
826 | "peerDependencies": {
827 | "vue": "3.5.13"
828 | }
829 | },
830 | "node_modules/@vue/shared": {
831 | "version": "3.5.13",
832 | "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
833 | "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
834 | },
835 | "node_modules/csstype": {
836 | "version": "3.1.3",
837 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
838 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
839 | },
840 | "node_modules/entities": {
841 | "version": "4.5.0",
842 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
843 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
844 | "engines": {
845 | "node": ">=0.12"
846 | },
847 | "funding": {
848 | "url": "https://github.com/fb55/entities?sponsor=1"
849 | }
850 | },
851 | "node_modules/esbuild": {
852 | "version": "0.25.2",
853 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
854 | "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
855 | "dev": true,
856 | "hasInstallScript": true,
857 | "bin": {
858 | "esbuild": "bin/esbuild"
859 | },
860 | "engines": {
861 | "node": ">=18"
862 | },
863 | "optionalDependencies": {
864 | "@esbuild/aix-ppc64": "0.25.2",
865 | "@esbuild/android-arm": "0.25.2",
866 | "@esbuild/android-arm64": "0.25.2",
867 | "@esbuild/android-x64": "0.25.2",
868 | "@esbuild/darwin-arm64": "0.25.2",
869 | "@esbuild/darwin-x64": "0.25.2",
870 | "@esbuild/freebsd-arm64": "0.25.2",
871 | "@esbuild/freebsd-x64": "0.25.2",
872 | "@esbuild/linux-arm": "0.25.2",
873 | "@esbuild/linux-arm64": "0.25.2",
874 | "@esbuild/linux-ia32": "0.25.2",
875 | "@esbuild/linux-loong64": "0.25.2",
876 | "@esbuild/linux-mips64el": "0.25.2",
877 | "@esbuild/linux-ppc64": "0.25.2",
878 | "@esbuild/linux-riscv64": "0.25.2",
879 | "@esbuild/linux-s390x": "0.25.2",
880 | "@esbuild/linux-x64": "0.25.2",
881 | "@esbuild/netbsd-arm64": "0.25.2",
882 | "@esbuild/netbsd-x64": "0.25.2",
883 | "@esbuild/openbsd-arm64": "0.25.2",
884 | "@esbuild/openbsd-x64": "0.25.2",
885 | "@esbuild/sunos-x64": "0.25.2",
886 | "@esbuild/win32-arm64": "0.25.2",
887 | "@esbuild/win32-ia32": "0.25.2",
888 | "@esbuild/win32-x64": "0.25.2"
889 | }
890 | },
891 | "node_modules/estree-walker": {
892 | "version": "2.0.2",
893 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
894 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
895 | },
896 | "node_modules/fsevents": {
897 | "version": "2.3.3",
898 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
899 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
900 | "dev": true,
901 | "hasInstallScript": true,
902 | "optional": true,
903 | "os": [
904 | "darwin"
905 | ],
906 | "engines": {
907 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
908 | }
909 | },
910 | "node_modules/magic-string": {
911 | "version": "0.30.17",
912 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
913 | "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
914 | "dependencies": {
915 | "@jridgewell/sourcemap-codec": "^1.5.0"
916 | }
917 | },
918 | "node_modules/nanoid": {
919 | "version": "3.3.11",
920 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
921 | "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
922 | "funding": [
923 | {
924 | "type": "github",
925 | "url": "https://github.com/sponsors/ai"
926 | }
927 | ],
928 | "bin": {
929 | "nanoid": "bin/nanoid.cjs"
930 | },
931 | "engines": {
932 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
933 | }
934 | },
935 | "node_modules/picocolors": {
936 | "version": "1.1.1",
937 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
938 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
939 | },
940 | "node_modules/postcss": {
941 | "version": "8.5.3",
942 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
943 | "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
944 | "funding": [
945 | {
946 | "type": "opencollective",
947 | "url": "https://opencollective.com/postcss/"
948 | },
949 | {
950 | "type": "tidelift",
951 | "url": "https://tidelift.com/funding/github/npm/postcss"
952 | },
953 | {
954 | "type": "github",
955 | "url": "https://github.com/sponsors/ai"
956 | }
957 | ],
958 | "dependencies": {
959 | "nanoid": "^3.3.8",
960 | "picocolors": "^1.1.1",
961 | "source-map-js": "^1.2.1"
962 | },
963 | "engines": {
964 | "node": "^10 || ^12 || >=14"
965 | }
966 | },
967 | "node_modules/rollup": {
968 | "version": "4.39.0",
969 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz",
970 | "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==",
971 | "dev": true,
972 | "dependencies": {
973 | "@types/estree": "1.0.7"
974 | },
975 | "bin": {
976 | "rollup": "dist/bin/rollup"
977 | },
978 | "engines": {
979 | "node": ">=18.0.0",
980 | "npm": ">=8.0.0"
981 | },
982 | "optionalDependencies": {
983 | "@rollup/rollup-android-arm-eabi": "4.39.0",
984 | "@rollup/rollup-android-arm64": "4.39.0",
985 | "@rollup/rollup-darwin-arm64": "4.39.0",
986 | "@rollup/rollup-darwin-x64": "4.39.0",
987 | "@rollup/rollup-freebsd-arm64": "4.39.0",
988 | "@rollup/rollup-freebsd-x64": "4.39.0",
989 | "@rollup/rollup-linux-arm-gnueabihf": "4.39.0",
990 | "@rollup/rollup-linux-arm-musleabihf": "4.39.0",
991 | "@rollup/rollup-linux-arm64-gnu": "4.39.0",
992 | "@rollup/rollup-linux-arm64-musl": "4.39.0",
993 | "@rollup/rollup-linux-loongarch64-gnu": "4.39.0",
994 | "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0",
995 | "@rollup/rollup-linux-riscv64-gnu": "4.39.0",
996 | "@rollup/rollup-linux-riscv64-musl": "4.39.0",
997 | "@rollup/rollup-linux-s390x-gnu": "4.39.0",
998 | "@rollup/rollup-linux-x64-gnu": "4.39.0",
999 | "@rollup/rollup-linux-x64-musl": "4.39.0",
1000 | "@rollup/rollup-win32-arm64-msvc": "4.39.0",
1001 | "@rollup/rollup-win32-ia32-msvc": "4.39.0",
1002 | "@rollup/rollup-win32-x64-msvc": "4.39.0",
1003 | "fsevents": "~2.3.2"
1004 | }
1005 | },
1006 | "node_modules/source-map-js": {
1007 | "version": "1.2.1",
1008 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1009 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1010 | "engines": {
1011 | "node": ">=0.10.0"
1012 | }
1013 | },
1014 | "node_modules/vite": {
1015 | "version": "6.2.5",
1016 | "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz",
1017 | "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==",
1018 | "dev": true,
1019 | "dependencies": {
1020 | "esbuild": "^0.25.0",
1021 | "postcss": "^8.5.3",
1022 | "rollup": "^4.30.1"
1023 | },
1024 | "bin": {
1025 | "vite": "bin/vite.js"
1026 | },
1027 | "engines": {
1028 | "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
1029 | },
1030 | "funding": {
1031 | "url": "https://github.com/vitejs/vite?sponsor=1"
1032 | },
1033 | "optionalDependencies": {
1034 | "fsevents": "~2.3.3"
1035 | },
1036 | "peerDependencies": {
1037 | "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
1038 | "jiti": ">=1.21.0",
1039 | "less": "*",
1040 | "lightningcss": "^1.21.0",
1041 | "sass": "*",
1042 | "sass-embedded": "*",
1043 | "stylus": "*",
1044 | "sugarss": "*",
1045 | "terser": "^5.16.0",
1046 | "tsx": "^4.8.1",
1047 | "yaml": "^2.4.2"
1048 | },
1049 | "peerDependenciesMeta": {
1050 | "@types/node": {
1051 | "optional": true
1052 | },
1053 | "jiti": {
1054 | "optional": true
1055 | },
1056 | "less": {
1057 | "optional": true
1058 | },
1059 | "lightningcss": {
1060 | "optional": true
1061 | },
1062 | "sass": {
1063 | "optional": true
1064 | },
1065 | "sass-embedded": {
1066 | "optional": true
1067 | },
1068 | "stylus": {
1069 | "optional": true
1070 | },
1071 | "sugarss": {
1072 | "optional": true
1073 | },
1074 | "terser": {
1075 | "optional": true
1076 | },
1077 | "tsx": {
1078 | "optional": true
1079 | },
1080 | "yaml": {
1081 | "optional": true
1082 | }
1083 | }
1084 | },
1085 | "node_modules/vue": {
1086 | "version": "3.5.13",
1087 | "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
1088 | "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
1089 | "dependencies": {
1090 | "@vue/compiler-dom": "3.5.13",
1091 | "@vue/compiler-sfc": "3.5.13",
1092 | "@vue/runtime-dom": "3.5.13",
1093 | "@vue/server-renderer": "3.5.13",
1094 | "@vue/shared": "3.5.13"
1095 | },
1096 | "peerDependencies": {
1097 | "typescript": "*"
1098 | },
1099 | "peerDependenciesMeta": {
1100 | "typescript": {
1101 | "optional": true
1102 | }
1103 | }
1104 | }
1105 | }
1106 | }
1107 |
--------------------------------------------------------------------------------