| examples | ||
| src | ||
| .gitignore | ||
| bun.lock | ||
| CLAUDE.md | ||
| package.json | ||
| README.md | ||
| server.tsx | ||
| tsconfig.json | ||
Forge
Why Forge?
CSS is powerful, but hostile to humans at scale.
Problems with CSS
- Styles are global and open — anything can override anything.
- There’s no link between a class in markup and its definition.
- Inline styles exist because there’s no structured way to vary styles per instance.
- Overrides are silent — conflicts happen without feedback.
- Complex components require selector gymnastics and reach-in styling.
What Forge Does Instead
- Styles are local to components and attached by generated handles, not strings.
- Parts give components named sub-targets without selectors.
- Variants replace inline styles with typed, declarative parameters.
- Style composition is deterministic (known merge order, last-wins).
- Overlapping changes are warned about in dev, not silently ignored.
What Forge Is
- A typed, local, variant-driven way to author CSS.
- A system that optimizes for people typing at a keyboard, not selectors in a cascade.
What Forge Is Not
- Not a new component model.
- Not a new language.
- Not a CSS replacement — it compiles to CSS, but removes the chaos.
Example:
import { define } from "forge"
export const Button = define("button", {
base: "button",
padding: 20,
background: "blue",
variants: {
kind: {
danger: { background: "red" },
warning: { background: "yellow" },
}
},
})
// Usage
<Button>Click me</Button>
<Button kind="danger">Click me carefully</Button>
<Button kind="warning">Click me?</Button>
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 (
<Root>
<Header>
<Avatar src={props.pic} />
<Bio>{props.bio}</Bio>
</Header>
</Root>
)
},
})
// Usage:
import { Profile } from './whatever'
<Profile pic={user.pic} bio={user.bio} />