TriFrost now includes a powerful, zero-runtime CSS engine — fully integrated with the core JSX renderer.
Write scoped, atomic, high-performance styles using the new css() utility. It supports pseudo-selectors, media queries, and deeply nested rules — all SSR-safe and deterministic.
Added
- feat:
css()— Inline, scoped styles with full support for pseudo-selectors, elements, and nested media queries - feat:
<Style />— Injects collected styles exactly where rendered (typically inside<head>) - feat: Rich selector API:
css.hover,css.nthChild(2),css.media.dark,css.before, etc. - feat:
css(obj, {inject: false})— Returns class name without injecting (useful for SSR hydration reuse) - feat:
css.root()— Register global styles or:rootvariables within component code
Example
// Theme.ts
import {css} from '@trifrost/core';
export function Theme () {
css.root({
'--radius': '4px',
[css.media.light]: {
'--color-bg': 'white',
'--color-fg': 'black',
'--color-button-bg': 'black',
'--color-button-fg': 'white',
},
[css.media.dark]: {
'--color-bg': 'black',
'--color-fg': 'white',
'--color-button-bg': 'white',
'--color-button-fg': 'black',
}
});
}// Button.tsx
import {css} from '@trifrost/core/jsx';
export function Button () {
const cls = css({
background: 'var(--color-button-bg)',
color: 'var(--color-button-fg)',
padding: '1rem 2rem',
borderRadius: 'var(--radius)',
fontWeight: 'bold',
[css.hover]: {filter: 'brightness(1.2)'},
[css.media.mobile]: {width: '100%'}
});
return <button className={cls}>Click me</button>;
}// Layout.tsx
import {css, Style} from '@trifrost/core';
import {Theme} from './Theme';
import {Button} from './Button.tsx';
export function Layout () {
Theme();
/* Body styles */
const cls = css({
background: 'var(--color-bg)',
color: 'var(--color-fg)',
});
return (<html>
<head>
<title>Styled Example</title>
{/* Style component where our collected styles will be injected */}
<Style />
</head>
<body className={cls}>
<main>
<h1>Hello World</h1>
<Button />
</main>
</body>
</html>);
}This renders:
<html>
<head>
<title>Styled Example</title>
<style>
:root {
--radius: 4px;
}
@media (prefers-color-scheme: light) {
:root {
--color-bg: white;
--color-fg: black;
--color-button-bg: black;
--color-button-fg: white;
}
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: black;
--color-fg: white;
--color-button-bg: white;
--color-button-fg: black;
}
}
.tf-abc123 {
background: var(--color-button-bg);
color: var(--color-button-fg);
padding: 1rem 2rem;
border-radius: var(--radius);
font-weight: bold;
}
.tf-abc123:hover {
filter: brightness(1.2);
}
@media (max-width: 600px) {
.tf-abc123 {
width: 100%;
}
}
.tf-def456 {
background: var(--color-bg);
color: var(--color-fg);
}
</style>
</head>
<body class="tf-def456">
<main>
<h1>Hello World</h1>
<button class="tf-abc123">Click me</button>
</main>
</body>
</html>You can also nest these:
import {css} from '@trifrost/core';
export function Card () {
const cls = css({
backgroundColor: 'white',
padding: '2rem',
borderRadius: '8px',
[css.hover]: {
boxShadow: '0 0 0 2px rgba(0,0,0,0.1)',
[css.media.dark]: {
boxShadow: '0 0 0 2px rgba(255,255,255,0.1)'
}
},
' h2': { fontSize: '1.25rem' },
' p': { fontSize: '1rem', color: 'gray' }
});
...
}Best Practices
- Always call
css()andcss.root()inside components or functions — styles are collected per request - Place
<Style />in<head> - Use
{inject: false}for reuse patterns (eg: infinite scroll when on the second page)
Improved
- misc: CICD tagged releases will now also automatically send a webhook message to discord announcing the release
- deps: Upgrade eslint to 9.27.0
- deps: Upgrade @cloudflare/workers-types to 4.20250510.0
- deps: Upgrade @types/node to 22.15.19
- deps: Upgrade @valkyriestudios/utils to 12.37.0
Bottom line:
- ✅ Fully deterministic and scoped
- ✅ Handles pseudo/media variants and nesting
- ✅ One class per style object
- ✅ Server-rendered, zero runtime
- ✅ No naming collisions — ever
Use it. Nest it. Compose it.
Note: The new css() engine is fully additive. It doesn’t replace native style usage — you can still use inline styles whenever it makes sense:
<div style={{backgroundColor: 'var(--color-bg)', color: 'var(--color-fg)'}}>...</div>Use css() for scoped, reusable, atomic styles — and reach for style={{...}} when you need one-off or dynamic values. Both work seamlessly together.