Kitten

This tutorial kicks off the Kitten Count series of tutorials (tutorials 2-9) that take you through the basics of Kitten by building and evolving an interactive page to count kittens.

Topics covered

You know what’s better than one kitten? Many kittens!

If you were following along from the previous tutorial, rename your index.html file to index.page.js (and otherwise create a new file called index.page.js) and update its contents to match the following:

let count = 1

export default () => kitten.html`
  <h1>Kitten count</h1>
  <p>${'đŸ±ïž'.repeat(count++)}</p>
`

Now run kitten, go to https://localhost, and refresh the page to see the number of kittens increase each time you do.

✹ Ooh, magic! ✹

How does it work?

A page in Kitten is written in plain old JavaScript (and hence the page.js extension).

Kitten uses file system routing. So, in this example, your /index.page.js file is mapped to the root route (/) of your site.

💡 Since Kitten pages – and components, etc., as you’ll see later – are just JavaScript files, you don’t need any extra tooling to make things with Kitten. Your editor of choice will likely already know how to work with JavaScript (and HTML and CSS) files or can be easily configured to do so.

A route in Kitten is just an exported default function inside of a JavaScript module. Any HTML you return from your route is sent to the browser.

You write your HTML inside of kitten.html tagged template literals. These are just standard JavaScript template literals (template strings) that get passed through a special global function called kitten.html that performs Kitten-specific magic.

🔒 Important security note: Part of that magic relates to the security of your apps. Kitten’s kitten.html tagged template literal sanitises any content you interpolate into your templates by default. This is important in avoiding cross-site-scripting (XSS) attacks. Kitten does the right thing by default when you use kitten.html but if you forget and return a snippet of HTML in a plain JavaScript string, for example, you will not be protected.

The example above contains the minimum code required to achieve our goal. However, here’s a more explicit version in case you’re not familiar with JavaScript arrow functions. In the version below, you can explicitly see everything that’s happening.

let count = 1

export default function route ({ request, response }) {
  return kitten.html`
    <h1>Kitten count</h1>
    <p>${'đŸ±ïž'.repeat(count++)}</p>
  `
}

With the above version of the code, it’s clear that what you’re exporting from the .page.js file is a route and that it gets passed a parameter object containing the HTTP request and response objects (which we are not using in this example).

Note that routes in Kitten take a parameter object (notice they are surrounded by { 
 }) instead of positional parameters which you might be familiar with from more traditional frameworks like Polka, Express, etc. Parameter objects make it easier to read code and are also used elsewhere in Kitten (components and fragments, the onConnect() handler, etc.)

Demystifying things

Take a look at the title of the tab when you run the example. Do you see that it reads “Kitten count”?

That’s odd, you didn’t tell Kitten to set the title of the page but if you look at the source code of the page, you’ll see that it’s set in the <head> of your page as:

<title>Kitten count</title>

How’d that happen?

Well, Kitten tries to do the right thing by default. So, if you don’t set a title for your page, it tries to derive one for you by looking to see if there is a <h1> tag on your page and using that if there is. It might not be perfect but it’s better than nothing if all you’re doing is coding up a quick example.

But what if you want to set your own title?

Enter, the page tag.

The <page> tag

Kitten has a special tag called <page> that you can use to set commonly found elements in the heads of your pages (lang, title, charset, icon, and viewport) as well as to include the libraries that Kitten has first-class support for (HTMX, HTMX idiomorph extension, HTMX WebSocket extension, Alpine.js, and Water.css).

To use it, you simply include the tag anywhere on a page (or fragment or component):

let count = 1

export default () => kitten.html`
  <page title='Kitten count example'>

  <h1>Kitten count</h1>
  <p>${'đŸ±ïž'.repeat(count++)}</p>
`

💡 You can also use the <page> tag to add classes to the body tag. e.g.,

<page bodyClass='someClass someOtherClass'>

If you run the example now, you’ll see that the title you supplied is shown instead of the default one derived from the <h1> element.

💡 In addition to the <html> tag’s lang attribute, the regular <head> settings, and supported libraries, you can also use the syntax-highlighter attribute to automatically use the default highlight.js light and dark mode themes (the WCAG AA accessible Measured light and dark themes) for code blocks rendered from <markdown>
</markdown> sections in your code. If you want to choose your own theme instead, you can override default themes by manually adding any of the roughly 500 highlight.js themes that exist to your project and specify the relative or absolute URL to them in the syntax-highlighter-theme-light and syntax-highlighter-theme-dark attributes.

💡 You’ll see later that you can set anything you want in the head of your pages using the special HEAD slot (<content for='HEAD'>
</content>) but the <page> tag should let you set the most common things you need when making Small Web sites and apps with Kitten.

As you can see, Kitten is very easy to get started with. But if you’re planning on creating an application that you see yourself supporting for a long time, you might be wondering about maintainability. Specifically, what if we want to make our code easier to write and debug by implementing type safety?

Kitten has you covered


Next tutorial: Type Safety

Like this? Fund us!

Small Technology Foundation is a tiny, independent not-for-profit.

We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.