Skip to content

Dxtobi/viteFrame

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

create-viteframe

Scaffold a new ViteFrame app — a lightweight SPA framework built on Vite with file-based routing, reactive state, and dynamic HTML components. No external JS dependencies.

Quick start

npm create viteframe@latest my-app
cd my-app
npm install
npm run dev

Or with pnpm / yarn:

pnpm create viteframe my-app
yarn create viteframe my-app

What you get

src/
├── core/
│   ├── router.js       # File-based SPA router (~180 lines)
│   ├── state.js        # Reactive state store (~100 lines)
│   └── components.js   # Dynamic component loader (~120 lines)
├── pages/
│   ├── landing.page.html          # → /
│   ├── about.page.html            # → /about
│   ├── docs.page.html             # → /docs
│   └── [id]post.page.html         # → /post/:id
├── components/
│   ├── header.comp.html
│   └── toast.comp.html
├── styles/
│   └── main.css        # Design tokens + utility classes
└── main.js             # Bootstrap

All framework code lives in your project — read it, modify it, own it.


Routing

File name determines the route. Drop files in src/pages/.

File Route
landing.page.html /
about.page.html /about
[id]post.page.html /post/:id
[id-slug]post.page.html /post/:id/:slug
[lang-id]post.page.html /post/:lang/:id

Programmatic navigation

window.__vf.navigate("/about");
window.__vf.navigate("/post/42", { replace: true });

State

Global state

Shared across all pages. Persists during navigation.

const { globalState } = window.__vf;

globalState.set("user", { name: "Alice" });
globalState.get("user");
globalState.subscribe("user", (val) => console.log(val));

// Bind value → DOM (auto-updates when state changes)
globalState.bind("user", el, (u) => u?.name ?? "Guest");

// Two-way bind: input ↔ state
globalState.bindInput("search", inputEl);

// Partial merge
globalState.merge("settings", { darkMode: true });

Page state

Isolated to a single page visit. Created inside onMount, discarded on navigation.

export function onMount() {
  const { createPageState } = window.__vf;
  const state = createPageState({ count: 0, open: false });

  state.update("count", (n) => n + 1);
  state.bind("count", document.getElementById("counter"));
}

Computed values

const { computed, globalState } = window.__vf

const fullName = computed(
  ['firstName', 'lastName'],
  [globalState, globalState],
  (first, last) => `${first} ${last}`
)

fullName.get()              // 'Alice Smith'
fullName.subscribe(v => )  // fires when either dep changes

Components

Components are .comp.html files in src/components/. They support scoped <style> and <script> blocks.

Use in HTML

<!-- Simple -->
<component src="header"></component>

<!-- With props via data-* attributes -->
<component src="card" data-title="Hello" data-body="World"></component>

Template syntax

<!-- src/components/card.comp.html -->
<div class="card">
  <h3>{{title}}</h3>
  <p>{{body}}</p>
</div>

Mount from JavaScript

const { mountComponent } = window.__vf;

await mountComponent("#target", "card", { title: "Hello", body: "World" });

Page lifecycle

Add <script data-lifecycle> to any page and export onMount:

<script data-lifecycle>
  export function onMount({ params, query, globalState }) {
    // params  → route params  e.g. { id: '42' }
    // query   → ?key=val      e.g. { tab: 'info' }
    // globalState → shared store

    console.log("id:", params.id);
    console.log("tab:", query.tab);

    const { createPageState } = window.__vf;
    const state = createPageState({ count: 0 });
  }
</script>

Navigation guards

In src/main.js:

router.beforeEach(({ pathname }) => {
  if (pathname.startsWith("/admin") && !globalState.get("user")) {
    router.navigate("/login");
    return false; // cancel navigation
  }
});

Toast notifications

Include <component src="toast"> on a page, then anywhere:

window.toast("Saved!", "success"); // green
window.toast("Something broke", "error"); // red
window.toast("FYI", "info", 5000); // purple, 5s

CSS design tokens

All colors, spacing, and radii are CSS variables in src/styles/main.css.

/* Override in your own CSS */
:root {
  --accent: #your-color;
  --radius: 12px;
}

Dark/light theme is toggled by setting data-theme="light" on <html>:

window.__vf.globalState.set("theme", "light");

License

MIT


Website

Github

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors