js and css, more

This commit is contained in:
Chris Wanstrath 2025-11-29 13:56:56 -08:00
parent c968c7aefc
commit 275527e30e
4 changed files with 73 additions and 14 deletions

View File

@ -88,6 +88,23 @@ CSS can be accessed via `/css/main.css`:
<link href="/css/main.css" rel="stylesheet" /> <link href="/css/main.css" rel="stylesheet" />
``` ```
Or written inline using the `css` template tag:
```tsx
import { css } from "hype"
export default () => (
<div>
{css`
* {
color: red;
}
`}
<h1>Hello</h1>
</div>
)
```
### css reset ### css reset
`hype` includes a css reset for your convenience: `hype` includes a css reset for your convenience:
@ -111,6 +128,25 @@ import { initAbout } from "./about"
import utils from "./shared/utils" import utils from "./shared/utils"
``` ```
Or written inline as transpiled typescript using the `js` template tag:
```tsx
import { js } from "hype"
export default () => (
<div>
{js`
window.onload = () => alert(welcomeMsg(Date.now()))
function welcomeMsg(time: number): string {
return "Welcome to my website!"
}
`}
<h1>Hello!</h1>
</div>
)
```
### pub ### pub
Anything in `pub/` is served as-is. Simple stuff. Anything in `pub/` is served as-is. Simple stuff.
@ -119,19 +155,22 @@ Anything in `pub/` is served as-is. Simple stuff.
`hype` includes helpful utils for your webapp: `hype` includes helpful utils for your webapp:
- capitalize - `capitalize(str: string): string` - Capitalizes a word
- darkenColor - `css` Template Tag - Lets you inline CSS in your TSX. Returns a `<style>` tag
- isDarkMode - `darkenColor(hex: string, opacity: number): string` - Darken a hex color by blending with black. opacity 1 = original, 0 = black
- lightenColor - `isDarkMode(): boolean` - Check if the user prefers dark mode
- rand - `js` Template Tag - Lets you inline JavaScript in your TSX. Transpiles and returns a `<script>` tag
- randIndex - `lightenColor(hex: string, opacity: number): string` - Lighten a hex color by blending with white. opacity 1 = original, 0 = white
- randItem - `rand(end = 2, startAtZero = false): number` - Generate random integer. `rand(2)` flips a coin, `rand(6)` rolls a die, `rand(20)` rolls d20
- randomId - `randIndex<T>(list: T[]): number | undefined` - Get a random index from an array. `randIndex([5, 7, 9])` returns `0`, `1`, or `2`
- randRange - `randItem<T>(list: T[]): T | undefined` - Get a random item from an array. `randItem([5, 7, 9])` returns `5`, `7`, or `9`
- redirectBack - `randomId(): string` - Generate a 6 character random ID
- transpile - `randRange(start = 0, end = 12): number` - Generate random integer in range. `randRange(1, 6)` rolls a die
- unique - `redirectBack(c: Context, fallback = "/"): Response` - Redirect to the referrer, or fallback if no referrer
- weightedRand - `times(n: number): number[]` - Generate array of integers. `times(3)` returns `[1, 2, 3]`
- `transpile(path: string): Promise<string>` - Transpile a TypeScript file at `path` to JavaScript (cached by mtime)
- `unique<T>(array: T[]): T[]` - Remove duplicates from array. `unique([1,1,2,2,3,3])` returns `[1,2,3]`
- `weightedRand(): number` - Generate random number between 1 and 10 with decreasing probability (1 is most likely, 10 is least)
## Setup ## Setup

View File

@ -6,6 +6,7 @@
"dependencies": { "dependencies": {
"hono": "^4.10.4", "hono": "^4.10.4",
"kleur": "^4.1.5", "kleur": "^4.1.5",
"prettier": "^3.7.3",
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",
@ -30,6 +31,8 @@
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"prettier": ["prettier@3.7.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],

View File

@ -14,6 +14,7 @@
}, },
"dependencies": { "dependencies": {
"hono": "^4.10.4", "hono": "^4.10.4",
"kleur": "^4.1.5" "kleur": "^4.1.5",
"prettier": "^3.7.3"
} }
} }

View File

@ -1,4 +1,5 @@
import { join } from 'path' import { join } from 'path'
import prettier from 'prettier'
import { type Context, Hono } from 'hono' import { type Context, Hono } from 'hono'
import { serveStatic } from 'hono/bun' import { serveStatic } from 'hono/bun'
import color from 'kleur' import color from 'kleur'
@ -9,6 +10,8 @@ import defaultLayout from './layout'
const SHOW_HTTP_LOG = true const SHOW_HTTP_LOG = true
const CSS_RESET = await Bun.file(join(import.meta.dir, '/reset.css')).text() const CSS_RESET = await Bun.file(join(import.meta.dir, '/reset.css')).text()
export * from './utils'
export class Hype extends Hono { export class Hype extends Hono {
routesRegistered = false routesRegistered = false
@ -38,6 +41,19 @@ export class Hype extends Hono {
console.log(fn(`${c.res.status}`), `${color.bold(method)} ${c.req.url} (${end - start}ms)`) console.log(fn(`${c.res.status}`), `${color.bold(method)} ${c.req.url} (${end - start}ms)`)
}) })
// prettify HTML output
if (process.env.NODE_ENV !== 'production') {
this.use('*', async (c, next) => {
await next();
if (c.res.headers.get('content-type')?.includes('text/html')) {
const html = await c.res.text();
const formatted = await prettier.format(html, { parser: 'html' });
c.res = new Response(formatted, c.res);
}
});
}
// css reset // css reset
this.get('/css/reset.css', async c => new Response(CSS_RESET, { this.get('/css/reset.css', async c => new Response(CSS_RESET, {
headers: { 'Content-Type': 'text/css' } headers: { 'Content-Type': 'text/css' }