Initial commit

This commit is contained in:
Astrian Zheng 2026-01-04 09:07:04 +11:00 committed by GitHub
commit f663a390e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 2863 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
*
# Metadata files
!readme.md
!UNLICENSE
# Config files
!package.json
!package-lock.json
!tsconfig.json
!.gitignore
!vite.config.ts
!uno.config.ts
!biome.json
# Backbone files
!index.html
# Assets files
!public/
!public/**/
!public/**/*.svg
!public/**/*.png
# Source code
!src/
!src/**/
!src/**/*.d.ts
!src/**/*.tsx
!src/**/*.css

24
UNLICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>

15
biome.json Normal file
View File

@ -0,0 +1,15 @@
{
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"javascript": {
"formatter": {
"semicolons": "asNeeded"
}
},
"linter": {
"enabled": true
}
}

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + TypeScript + WebJSX</title>
</head>
<body>
<x-app></x-app>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

2538
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

25
package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "webcomponents-initial-template",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "biome check .",
"format": "biome format --write .",
"check": "biome check --write ."
},
"devDependencies": {
"@biomejs/biome": "2.2.4",
"@unocss/preset-attributify": "^66.5.2",
"typescript": "~5.8.3",
"unocss": "^66.5.2",
"vite": "^7.0.4"
},
"dependencies": {
"@unocss/reset": "^66.5.2",
"webjsx": "^0.0.73"
}
}

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/webjsx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

47
readme.md Normal file
View File

@ -0,0 +1,47 @@
# Vite + WebJSX Template Repository
![Screenshot](https://s2.loli.net/2025/10/02/eiYOHwm9E23gRhZ.png)
This is a template repository for creating an initialized front-end project. Clone this template and those libraries are pre-installed and pre-configured without further actions:
- [Vite](https://vite.dev)
- [TypeScript](https://www.typescriptlang.org)
- [WebJSX](https://webjsx.org) with [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) support (Shadow DOM not included)
- [UnoCSS](https://unocss.dev) with Tailwind CSS-reset presets and attributify mode enabled
- [Biome](https://biomejs.dev) with tab character indentation (instead of spaces) and avoiding semicolons
- `.gitignore` file with whitelist mode
## How to Use
- Click `Use this template` at the top-right, then `Create a new repository`
- Fill in the information
- Clone the new project to your local machine
- `npm i` then `npm run dev`
- See the template on your browser
Dont forget to:
- Modify this `readme.md` file
- Modify the name in `package.json` config
- Check the `.gitignore` (since it is in whitelist mode)
- Check the preset of `biome.json`, and set the git hook
- Remove unlicense statement and replace it as your own (if necessary)
## But Why
The vanilla-flavored (a.k.a. native environment) front-end is more powerful than we expected, especially for people who have a lot of experience with “modern frameworks” like React, Vue, Angular, etc. Web Components is a really powerful technology and you should have a try and consider replacing the bulky frameworks.
I made a lightweight framework (which in learning purpose) based on Web Components technology called [Laterano](https://codeberg.org/Astrian/Laterano) and works fine. However, it doesnt resolve an issue to traditional Web Components. For example, the editor cannot highlight the syntax of inline HTML strings, also it cannot check the type of the components, etc.
Thats why WebJSX was introduced. WebJSX is a JSX/TSX transpiler instead of a fully functional front-end framework. You can enjoy the benefits from JSX/TSX language, such as HTML syntax highlighting, list rendering and conditional rendering, type check and more. I believe it is a great improvement of the development experience of Web Components (although I am actually not a fan of JSX/TSX language).
Since Vite doesnt provide the project initialize preset of Web Components/WebJSX, I created this template repository to avoid re-configurating when I create a new front-end project.
For UnoCSS and Biome, it just my personal preference.
UnoCSS is a lighter version of Tailwind and give you a larger flexibility of styling and higher performance on CSS generating. Same reason of choosing Biome instead of Prettier.
You may notice I prefer to use tab character and avoid semicolons as indentation. This is a very personal choice, which I believe it can save more storage no matter on local machine or remote hosting. Also I think tab character can let user to choose their indentation length instead of fixed 2 or 4 width.
You can clone and get rid of all my personal quirk anytime.
## (Un)license
This template is totally in public domain and anyone can use, modify and distribute the template as they wish. See [UNLICENSE](./UNLICENSE)

43
src/counter.tsx Normal file
View File

@ -0,0 +1,43 @@
import { applyDiff } from "webjsx"
declare global {
namespace JSX {
interface IntrinsicElements {
"x-counter": {
onclick?: () => void
}
}
}
}
class CounterComp extends HTMLElement {
counter = 0
connectedCallback() {
this.render()
}
render = () => {
const content = (
<button
type="button"
rounded="0.5rem"
border="~ transparent hover:#646cff"
p="y-0.6rem x-1.2rem"
font="medium"
bg="#f9f9f9 dark:#1a1a1a"
onClick={this.counterPlus}
>
Count is {this.counter}
</button>
)
applyDiff(this, content)
}
counterPlus = () => {
this.counter++
this.render()
}
}
customElements.define("x-counter", CounterComp)

52
src/main.tsx Normal file
View File

@ -0,0 +1,52 @@
import { applyDiff } from "webjsx"
import viteLogo from "/vite.svg"
import webjsxlogo from "/webjsx.png"
import "@unocss/reset/tailwind.css"
import "virtual:uno.css"
import "./style.css"
import "./counter"
class App extends HTMLElement {
constructor() {
super()
this.render()
}
render = () => {
const content = (
<div w="100vw">
<div
max-w="80rem"
m="y-0 x-auto"
p="8"
un-text="align-center"
flex="~ col"
items="center"
>
<div flex="~">
<a href="https://vite.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} alt="Vite logo" h="24" p="6" />
</a>
<a href="https://webjsx.org/" target="_blank" rel="noreferrer">
<img src={webjsxlogo} alt="TypeScript logo" h="24" p="6" />
</a>
</div>
<h1 font="medium" un-text="3.2rem">
Vite + TypeScript + WebJSX
</h1>
<div class="card">
<button id="counter" type="button"></button>
</div>
<div p="8">
<x-counter />
</div>
<p text="#888">Click on the Vite and WebJSX logos to learn more</p>
</div>
</div>
)
applyDiff(this, content)
}
}
customElements.define("x-app", App)

29
src/style.css Normal file
View File

@ -0,0 +1,29 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 20rem;
min-height: 100vh;
}

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

26
tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* JSX Configuration for WebJSX */
"jsx": "react-jsx",
"jsxImportSource": "webjsx",
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

10
uno.config.ts Normal file
View File

@ -0,0 +1,10 @@
import { defineConfig, presetAttributify, presetWind3 } from "unocss"
export default defineConfig({
presets: [
presetWind3({
dark: "media",
}),
presetAttributify({}),
],
})

9
vite.config.ts Normal file
View File

@ -0,0 +1,9 @@
import UnoCSS from "unocss/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [UnoCSS()],
esbuild: {
jsxImportSource: "webjsx",
},
})