i80 is a stylesheet and a set of <i80-*> custom elements. You load two files and write
HTML. There is no framework, no build step and no package to install, so the same markup
works in a static page, in a Django or Rails template, and in whatever you write next.
Load i80
Put the stylesheet and the script in your page's <head>:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://design.i80.dk/v/0.5.2/components.css">
<script type="module" src="https://design.i80.dk/v/0.5.2/i80.js"></script>
</head>
<body>
<i80-button color="primary" icon="plus">New plan</i80-button>
</body>
</html>
That is the whole setup. components.css brings the design tokens, the component styles
and the helper classes; i80.js teaches the browser the <i80-*> tags and fetches the
icons a page actually uses.
Pin a version
https://design.i80.dk/latest/ follows the design system as it changes, which is right
for a prototype and wrong for a product. Every release is frozen under its own version, so
pin one and upgrade when you choose:
| URL | Use it for |
|---|---|
https://design.i80.dk/v/0.5.2/… |
Products. Frozen: the files never change. |
https://design.i80.dk/latest/… |
Prototypes, and trying a change out. |
Or serve the files yourself
Nothing needs the CDN. Copy the files into your application and serve them from your own domain, which also keeps the page working when design.i80.dk does not:
# Serve the files from your own application instead of the CDN
V=0.5.2
mkdir -p static/i80
curl -sSLo static/i80/components.css https://design.i80.dk/v/$V/components.css
curl -sSLo static/i80/i80.js https://design.i80.dk/v/$V/i80.js
curl -sSL https://design.i80.dk/v/$V/icons.tar.gz | tar -C static/i80 -xzf -
Then point the icons at your copy, since i80.js looks for them next to itself:
<script type="module">
import { setIconBase } from "/static/i80/i80.js";
setIconBase("/static/i80/icons/");
</script>
Your first component
Write the tag, give it attributes, put the text inside it:
<i80-button color="primary" icon="plus">New plan</i80-button>
<i80-button color="secondary">Cancel</i80-button>
<i80-badge color="success" icon="check">Running</i80-badge>
Each component's page on this site shows the same thing for every example it documents: open the Code tab and copy the HTML. The full list of tags with live examples is on one page, and a whole screen built from them shows them working together.
Forms keep working
This is the part that matters in a server-rendered application. The elements use no Shadow
DOM, and a native element you write inside a tag is kept and only styled. So the <input>
in your form is your <input>: it keeps its name, id, value, validation state and
CSRF token, <label for> points at it, and the form submits as it always did.
<i80-text-input label="Email" error="{{ form.email.errors.0 }}">
{{ form.email }} <!-- Django renders <input type="email" name="email" id="id_email"> -->
</i80-text-input>
The same applies to <a>, <button> and <form> inside a tag, which is how a destructive
action stays a real form post:
<i80-dropdown text="Actions">
<i80-dropdown-item color="critical" icon="trash">
<form method="post" action="/plans/7/delete">
{% csrf_token %}<button>Delete</button>
</form>
</i80-dropdown-item>
</i80-dropdown>
Because the markup inside a tag is real HTML, a page is usable before i80.js has loaded,
and still usable if it never does: the form submits, the link navigates, the button posts.
Icons
Icons come with components.css and i80.js; there is nothing else to install. Most
components take an icon attribute, and <i80-icon> renders one on its own:
<i80-button icon="plus">New plan</i80-button>
<i80-icon name="alert-triangle" size="24" color="warning" title="Needs attention"></i80-icon>
Only the icons a page uses are downloaded, one small file each. Browse the library for the names.
An icon with a title is announced by a screen reader as an image with that name; without
one it is decorative and hidden, which is what you want next to a label that already says
the same thing.
Tokens and helper classes
The tokens are CSS variables, so your own components can be built from the same values:
/* The tokens components.css declares are yours to use */
.plan-card {
color: var(--i80-color-foreground-primary);
background: var(--i80-color-surface-primary);
border: 1px solid var(--i80-color-border-primary);
border-radius: var(--i80-border-radius-medium);
box-shadow: var(--i80-elevation-low-box-shadow);
font-family: var(--i80-typography-font-stack-text);
}
And every component style is a class, so you can write the markup yourself where an element would only get in the way — a static heading, a button inside a template you do not control:
Plans
Eight plans, two of them shared.
<p class="i80-typography-display-300 i80-foreground-strong">Plans</p>
<p class="i80-typography-body-200 i80-foreground-faint">Eight plans, two of them shared.</p>
<button class="i80-button i80-button--color-primary i80-button--size-medium" type="button">
<span class="i80-button__text">Save</span>
</button>
Read the tokens documentation for the full set, and Themes for changing them all at once.
In a framework
Custom elements are part of the browser, so the tags work anywhere HTML is rendered: HTMX, Alpine and Turbo need nothing, React 19, Svelte, Solid and Lit need nothing, and Vue and Angular need one line of configuration. Using i80 in a framework has the details per framework.
Browser support
| Browser | Version |
|---|---|
| Chrome | last 2 versions |
| Safari | last 2 versions |
| Firefox | last 2 versions |
| Microsoft Edge | last 2 versions |
For AI coding agents
If an agent writes code in your project, give it the two files written for that purpose: llms.txt explains how i80 is used in one page, and components.json lists every tag with its attributes, values and defaults.
# What an AI coding agent needs: how to use i80, and every tag with its attributes
curl -sS https://design.i80.dk/llms.txt
curl -sS https://design.i80.dk/elements/components.json | jq '.[] | .tag'
Working on the design system itself
The site you are reading is built from an Ember codebase — it is a fork of HashiCorp's
Helios, which is where the component styles and the icon set come from. You never need any
of that to use i80 in a product; it only matters if you are changing the design system.
The repository is at gea.i80.dk, and
elements/README.md in it describes how the <i80-*> tags are built and tested.