A ridiculously small, pseudo-dynamic, fully client-side web framerwork with a RESTful API.
This is the source for the attoweb.org website.
NOTE: At times this link may lag behind the attoweb.org about page.
I created this project in order to build my own website. It served a number of purposes for me. I wanted to:
- use a super-lean, markdown-based microframework.
- use GitHub as my editing UI.
- easily deploy on shared hosting where I do not have access to reverse-proxy ports.
- take the opportunity to translate my resume, cv, and work-samples to Markdown.
- build it without having to learn a client-side framework (for the time being) such as React or Angular.
- challenge myself to build something useful in JavaScript.
Web micro-frameworks serve as very purpose-driven website building tools. There are a number of more-or-less easy to use static site generators out there. However, they did not fit my needs. I wanted something even smaller, and more importantly, does not run as a micro-server such as apps written in Python, NodeJS, Ruby, Go, etc. These all run on dedicated ports (e.g. 8080) and need to be reverse-proxied to a domain name.
However, JavaScript really has all I need:
- AJAX files from the server (or fGitHub repository).
- Render the markdown to HTML.
- Style the HTML with CSS.
So, I started scribbling with JavaScript. I came up with the idea of using a fake-ish RESTful api so that I didn't have to hard-code any paths. I later found out this is called "client side routing." I got it to basically work, and decided it was worth using for my website.
I needed a name for this thing for GitHub. It's definitely smaller than a microframework. The names PicoWeb and FemtoWeb web have already been taken. Next in line was AttoWeb.
All you need in the HTML is some containers for the content. For exmaple
<body>
<h1>My Website</h1>
<main id='main'></main>
</body>
Suppose you set up the app to originally render initial.md into #main
:
initial.md
## Title
Some content including a [link](?source=content/other.md&target=main)
becomes
<h2>Title</h2>
<p>Some content including a <a href="?source=content/other.md&target=main">link</a></p>
When you click on the link, first atto catches the click event and stops the default behavior with preventDefault
which would trigger a page reload. Then atto parses out the query. It creates a full url that contains the home url (e.g. http://exmple.com/mysite). Finally atto does two thangs that characterize how ti works:
- It makes a call to the
updatePage
function passing in the query. This function AJAXes down the Markdown source fromhttp://\[........\]/content/other.md
, renders it into HTML and sticks it in the element with ID#main
. - It update what is in the url bar using
window.history.pushState
. This does NOT trigger a page reload. But it does make it look like you are on a new page, and also updates the browser history so the back and forwards buttons work.
For slightly more advanced usage, there is a simple plugin system which I used to create the responsive nav in my personal website as well as the simple dropdown nav on the AttoWeb website.
Note: The plugin system is currently being implemented as page update callbacks. Once I get this smoothed out, instructions will follow.
There is also an optional simple routing system that lets you create pre-defined queries to simplify links and other content queries. For instance, you can specify
somelink: {path: "content", source: "somepage.md", target: "main"}
so that you can have a link such as [link](somelink)
which will act the same as the link [link](?target=main&source=content/somepage.md)
.
The entire app is contained in single JavaScript file of (currently) roughly 300 lines, at least 100 of which are comments and debuging printouts.
That's as far as I plan to take it. At least I hope so. Otherwise it will grow into a femto-framework, burgeon into a pico-framework, and finally bloat into a micro-framework. And who wants that?
To do:
- attoweb-basics
- directory structure
- config file
- routing
- default pages
- initial page
- themes page
- quickstart page
- plugins -- describe nav plugin. go over plugin loop
- Make hashes for in-page links work. Will probably require page scrolling to the desired point in the page.