# ⚒️ forge
```
╔═╝╔═║╔═║╔═╝╔═╝
╔═╝║ ║╔╔╝║ ║╔═╝
╝ ══╝╝ ╝══╝══╝
```
## overview
Forge is a typed, local, variant-driven way to organize CSS and create
self-contained TSX components out of discrete parts.
## css problems
- Styles are global and open - anything can override anything anywhere.
- No IDE-friendly link between the class name in markup and its definition.
- All techniques are patterns a human must know and follow, not APIs.
- Errors happen silently.
## forge solutions
- All styles are local to your TSX components.
- Styles defined using TS typing.
- Component styles are made up of independently styled "Parts".
- "Variants" replace inline styles with typed, declarative parameters.
- Style composition is deterministic.
- Themes are easy.
- Errors and feedback are provided.
## examples
### styles
```tsx
import { define } from "forge"
export const Button = define("button", {
base: "button",
padding: 20,
background: "blue",
})
// Usage
```
### variants
```tsx
import { define } from "forge"
export const Button = define("button", {
base: "button",
padding: 20,
background: "blue",
variants: {
status: {
danger: { background: "red" },
warning: { background: "yellow" },
}
},
})
// Usage
```
### parts + `render()`
```typescript
export const Profile = define("div", {
padding: 50,
background: "red",
parts: {
Header: { display: "flex" },
Avatar: { base: "img", width: 50 },
Bio: { color: "gray" },
},
variants: {
size: {
small: {
parts: { Avatar: { width: 20 } },
},
},
},
render({ props, parts: { Root, Header, Avatar, Bio } }) {
return (
{props.bio}
)
},
})
// Usage:
import { Profile } from "./whatever"
console.log()
console.log()
```
### selectors
Use `selectors` to write custom CSS selectors. Reference the current
element with `&` and other parts with `@PartName`:
```tsx
const Checkbox = define("Checkbox", {
parts: {
Input: {
base: "input[type=checkbox]",
display: "none",
},
Label: {
base: "label",
padding: 10,
cursor: "pointer",
color: "gray",
selectors: {
// style Label when Input is checked
"@Input:checked + &": {
color: "green",
fontWeight: "bold",
},
// style Label when Input is disabled
"@Input:disabled + &": {
opacity: 0.5,
cursor: "not-allowed",
},
},
},
},
render({ props, parts: { Root, Input, Label } }) {
return (
)
},
})
// Usage
```
## themes
built-in support for CSS variables with full type safety:
```tsx
// themes.tsx - Define your themes
import { createThemes } from "forge"
export const theme = createThemes({
dark: {
bgColor: "#0a0a0a",
fgColor: "#00ff00",
sm: 12,
lg: 24,
},
light: {
bgColor: "#f5f5f0",
fgColor: "#0a0a0a",
sm: 12,
lg: 24,
},
})
// Use theme() in your components
import { define } from "forge"
import { theme } from "./themes"
const Button = define("Button", {
padding: theme("spacing-sm"),
background: theme("colors-bg"),
color: theme("colors-fg"),
})
```
Theme switching is done via the `data-theme` attribute:
```tsx
// Toggle between themes
document.body.setAttribute("data-theme", "dark")
document.body.setAttribute("data-theme", "light")
```
The `theme()` function is fully typed based on your theme keys, giving
you autocomplete and type checking throughout your codebase.
## scopes
Sometimes you want your parts named things like ButtonRow, ButtonCell,
ButtonTable, etc, but all those Button's are repetitive:
```typescript
const { define } = createScope("Button")
// css class becomes "Button"
const Button = define("Root", {
// becomes "Button"
// ...
})
// css class becomes "ButtonRow"
const ButtonRow = define("Row", {
// ...
})
// css class becomes "ButtonContainer"
const ButtonContainer = define("Container", {
// ...
})
```
## see it
Check out the `examples/` dir and view them at http://localhost:3300 by
cloning this repo and running the local web server:
```
bun install
bun dev
open http://localhost:3300
```