TriFrost

TriFrost 1.2.0

|peterver

News

This update brings quality-of-life enhancements to TriFrost’s client-side utilities, focused on ergonomics and resilience in dynamic UIs.

Added

  • feat: $.goto utility, a high-level navigation helper for declarative and ergonomic client-side routing with built-in handling for query merging, blank tab opening, and replace-mode navigation.
$.goto("/dashboard");
// → Navigates to: /dashboard
$.goto("/login", "replace");
// → Replaces current history entry with /login
$.goto("https://external.site", "blank");
// → Opens https://external.site in a new tab
// Current url: https://app.local/settings?page=2&theme=dark

$.goto("/account", "query");
// → Navigates to: /account?page=2&theme=dark

$.goto("/search?q=test", "query");
// → Navigates to: /search?q=test&page=2&theme=dark

$.goto("/search?q=test&page=3", "query");
// → Navigates to: /search?q=test&page=3&theme=dark

$.goto("/profile", {
  replace: true,
  includeQuery: true
});
// → Replaces history with: /profile?page=2&theme=dark
  • feat: Default CSS media breakpoints now includes tabletUp, allowing you to target tablet and above. The current set of default breakpoints is now:
css.media.mobile /* <= 600px */
css.media.tablet /* <= 1199px */
css.media.tabletOnly /* > 600px AND < 1200px */
css.media.tabletUp /* > 600px */
css.media.desktop /* >= 1200px */

Where previously you'd do something like:

css({
  [css.media.tablet]: {flexDirection: 'row'},
  [css.media.desktop]: {flexDirection: 'row'},
  [css.media.mobile]: {flexDirection: 'column'},
})

You can now shorten this to:

css({
  [css.media.tabletUp]: {flexDirection: 'row'},
  [css.media.mobile]: {flexDirection: 'column'},
})

Improved

  • deps: Upgrade @cloudflare/workers-types to 4.20250722.0
  • deps: Upgrade @types/node to 22.16.5
  • deps: Upgrade bun-types to 1.2.19
  • deps: Upgrade eslint-config-prettier to 10.1.8
  • deps: Upgrade eslint-plugin-prettier to 5.5.3
  • deps: Upgrade typescript-eslint to 8.38.0
  • feat: $.storeSet now short-circuits when provided value equals the current value, reducing unnecessary storage operations.
  • feat: $bind avoids re-binding already-bound inputs if passed multiple times, preventing redundant event wiring.
  • feat: $.uid now falls back to Date.now() + RNG if crypto.randomUUID is unavailable
  • feat: Automatic Cleanup for $.on event listeners. Listeners are now auto-unregistered when a VM unmounts, an element is detached from the DOM, dynamic elements created via $.create are removed.
$.on Before (Manual Cleanup)
const listener = $.on(window, "resize", () => {
  console.log("Resized");
});

el.$unmount = () => {
  listener();
};
$.on After (Automatic Cleanup)
$.on(window, "resize", () => {
  console.log("Resized");
});

// No manual cleanup needed — handled by the VM and node itself

The same applies to:

  • Element listeners ($.on(el, "click", ...))
  • Global listeners ($.on(window, "keydown", ...))
  • Listeners added to dynamically created elements using $.create("div")

These changes reduce boilerplate and improve durability, especially in ephemeral or reactive UI flows.

As always. Stay frosty. ❄️

Loved the read? Share it with others