# Installation

There is nothing to install. Each piece is plain files you copy into your project.

## 1. Sign in

There is no key. You buy while signed in with Google or GitHub, and the Pass lives on that account: sign in the same way on any device and everything is open. Your account page downloads everything at once.

## 2. Download a package

Your account page downloads everything you own as one file, `tactile-ui.zip`: every object and block with a Pass, the `kit/` folder with the Kit, both with the Founder Pass, and the free pieces with any account. On any piece's page, **Code** then **Download package** gets that piece alone. Every zip unzips to one folder named like the file. A package for the Flip-Dot looks like this:

```text
flip-dot-0.1.0/
├─ README.md                start here: the steps below, in full
├─ flip-dot.html            the runnable example, at the settings you chose
├─ flip-dot/
│  ├─ flip-dot.mjs          the object
│  └─ flip-dot.css          its styles
├─ prompts/
│  ├─ flip-dot.reproduce.md
│  └─ flip-dot.remix.md
├─ frameworks/              the same example for React, Vue, Svelte, a custom element
├─ wrappers/                the few lines those import
├─ index.html               links every example in the folder
├─ manifest.json            every file with its SHA-256
├─ verify.mjs               checks every file against manifest.json
├─ LICENCE.md
└─ serve.mjs                a tiny local server
```

A block's package also contains the folder of the object it is built with. `tactile-ui.zip` also has `start.html`, a first page with an object, a block and, with the Kit, three kit parts, and its `README.md` walks through it and through a fresh React app.

## 3. Check it and run the example

Inside the unzipped folder, `node verify.mjs` checks every file against `manifest.json` and prints `all N files match manifest.json`, or names the file that does not.

ES modules need a real web server: a browser won't import them from `file://`. Inside the unzipped folder:

```bash
node serve.mjs
```

Then open the address it prints: it lists every example. Add the example's file name to go straight to it, for example `http://127.0.0.1:PORT/flip-dot.html`. Any static server works as well.

## 4. Use it in your page

Copy the object's folder into your project, link its stylesheet and construct it on an element. The options are exactly the ones the Code tab shows.

```html
<link rel="stylesheet" href="/flip-dot/flip-dot.css">
<div id="sign"></div>
<script type="module">
  import { FlipDot } from '/flip-dot/flip-dot.mjs';

  const sign = new FlipDot(document.getElementById('sign'), {
    text: 'NOW BOARDING',
    disc: 'fluorescent',
  });

  sign.setText('PLATFORM 4');     // the object's own verbs
  sign.update({ disc: 'white' }); // any option, any time
  // sign.destroy();              // removes listeners, observers and the canvas
</script>
```

Every object follows the same shape: `new Object(element, options)`, `update(options)`, `destroy()`. Each one sizes itself to its element, pauses when it is off screen and sleeps when nothing is moving.

## Blocks

A block exports `mount(element, options, dependencies)` and is handed its object's class, so the same files work wherever you put them:

```js
import { FlipDot } from '/flip-dot/flip-dot.mjs';
import { mount } from '/departures-board/departures-board.mjs';

const board = mount(document.getElementById('board'), { /* the Code tab's options */ }, { FlipDot });
// board.update({...}); board.destroy();
```

Next: [Using a framework](/docs/frameworks).

## Installing the kit

The complete kit folder, a first-paint finish script and a class on the root. No build step, no framework, no runtime dependencies.

### 1. Copy the files

Your account page downloads `tactile-ui.zip` with the Kit or the Founder Pass; it unzips to a `tactile-ui/` folder whose `README.md` starts with a working page that uses the kit. Copy the entire `kit/` folder from it into your project root, preserving every relative path and font licence. The entry module imports `finishes.mjs` and `parts/`; themes and the brain are needed too. Copying only the three files shown on an individual part page is not a complete kit installation.

```
your-site/
  kit/
    kit.css          styles, tokens and @font-face
    kit.mjs          behaviour entry module
    finishes.mjs     finish registry and switching
    parts/           modules imported by kit.mjs
    themes/          Wood, Materials, Light, Printed and Screens sheets
    fonts/           WOFF2 faces, with OFL licences
    brain/           rules, inventory, skills and verify tool
```

### 2. The fonts

The kit needs two typefaces, and they ship with it: nothing is fetched from a font service. **Instrument Sans** (regular, italic, medium, semibold, bold) sets everything, with keys and labels at medium and semibold; **Geist Mono** (regular, bold) appears only inside displays. Both are under the SIL Open Font Licence; keep the two `-OFL.txt` files with them. The Printed and Screens families (`kit/themes/printed.css`, `kit/themes/screens.css`) add a third, **Departure Mono** (regular, OFL too), and load it themselves, so only a page in paper, phosphor, e-paper or AMOLED fetches it.

```
kit/fonts/InstrumentSans-Regular.woff2
kit/fonts/InstrumentSans-Italic.woff2
kit/fonts/InstrumentSans-Medium.woff2
kit/fonts/InstrumentSans-Medium-Ext.woff2
kit/fonts/InstrumentSans-SemiBold.woff2
kit/fonts/InstrumentSans-SemiBold-Ext.woff2
kit/fonts/InstrumentSans-Bold.woff2
kit/fonts/GeistMono-Regular.woff2
kit/fonts/GeistMono-Bold.woff2
kit/fonts/DepartureMono-Regular.woff2   Printed and Screens only
kit/fonts/CourierPrime-Regular.woff2    the Writer template's sheet only (OFL)
```

If you serve the stylesheet from somewhere else, edit the `url('./fonts/…')` lines at the top of `kit.css` (the `-Ext` files carry Latin Extended and load only when a page needs them). To preload the body face:

```
<link rel="preload" href="/kit/fonts/InstrumentSans-Regular.woff2" as="font" type="font/woff2" crossorigin>
```

### 3. The head

Put `class="tui"` on `<html>`, and this script first in the head. It picks graphite when the system is dark and aluminium otherwise, before the first paint, and honours a choice the visitor made earlier.

```
<html lang="en" class="tui">
<head>
  <meta charset="utf-8">
  <script>(function(){var d=document.documentElement,F={'machined':['aluminium','graphite'],'wood':['oak','walnut'],'materials':['porcelain','black'],'light':['daylight','night'],'printed':['paper','phosphor'],'screens':['e-paper','amoled']},f,m,t,k;try{f=localStorage.getItem('tui-finish');m=localStorage.getItem('tui-family')}catch(e){}for(k in F)if(F[k].indexOf(f)>=0){m=k;t=F[k].indexOf(f)?'dark':'light'}if(!F[m])m='machined';t=t||(matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');d.dataset.finish=F[m][t==='dark'?1:0];d.dataset.tone=t;var w;try{w=localStorage.getItem('tui-wood')}catch(e){}if(w==='trims'||w==='full'||w==='inverted')d.dataset.wood=w})()</script>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="/kit/kit.css">
  <link rel="stylesheet" href="/kit/themes/wood.css"><link rel="stylesheet" href="/kit/themes/materials.css"><link rel="stylesheet" href="/kit/themes/light.css"><link rel="stylesheet" href="/kit/themes/printed.css"><link rel="stylesheet" href="/kit/themes/screens.css">
</head>
```

Keep all four theme links to use non-machined finishes. These URLs assume the site root; for a page served below a path, use `./kit/` instead of `/kit/` and keep the kit next to that page.

### 4. A first panel

Every part is a class on a native element. This settings panel is the markup below, live:

Recording Ready Phantom power Take name

```
<div class="tui-panel">
  <div class="tui-panel__head">
    <span class="tui-label">Recording</span>
    <span class="tui-status"><span class="tui-lamp" data-on></span>Ready</span>
  </div>
  <label class="tui-choice">Phantom power <input type="checkbox" class="tui-switch" checked></label>
  <label class="tui-field">
    <span class="tui-label">Take name</span>
    <input class="tui-input" value="Harbour survey">
  </label>
  <button class="tui-key tui-key--primary">Start recording</button>
</div>
```

Need the knob, tabs or toasts? Add one line at the end of the body: `<script type="module" src="/kit/kit.mjs"></script>`.

### 5. Check it

Run the brain’s verify tool on the page. It reads the markup and reports anything that breaks a law: a colour literal, an accent hue, two contrast keys on one surface, an unlabelled control, a display ghost of the wrong width, a missing finish script.

```
node kit/brain/tools/verify.mjs index.html
```

Then look at it in every finish and at 390px wide. [Using it with Claude and Cursor](https://tactileui.dev/docs/agents) goes further.

Use HTTP rather than opening an HTML file directly: browser modules do not work reliably under `file://`. The part's Copy prompt includes a complete `index.html` and dependency-free `serve.mjs` for a fresh project. It requires Node.js 18 or newer. Run `node serve.mjs` from the project root, open `http://127.0.0.1:5173/`, and stop it with Ctrl+C.
