Developers

Pulsar UI kit

For your own HTML pages: popups, options and full pages. One CSS file with the app’s exact colours, sizes and motion, a tiny script for the parts that move, and the icon set. No framework.

Demos in

Set up

Copy the three files into your extension (pages inside an extension can’t load them from the web), then:

options.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <meta name="color-scheme" content="light dark">
  <link rel="stylesheet" href="ui/pulsar-ui.css">
</head>
<body class="pl-page">
  <main class="pl-wrap">
    …
  </main>
  <script type="module" src="ui/pulsar-ui.js"></script>
</body>
</html>
  • pl-page on <body> sets the font, colours and ground. Use pl on any element instead to scope it.
  • Dark mode follows the phone. Set data-theme="light" or "dark" on <html> (or call PulsarUI.theme()) to force one.
  • Icons: <svg class="pl-icon"><use href="ui/pulsar-icons.svg#shield"/></svg>. Pulsar’s font is Inter; bundle it if you want it, else the phone’s own font is used.
  • Right-to-left works: the kit uses logical properties. Add pl-flip to arrows that should point the other way.

pulsar-ui.js

An ES module that also sets window.PulsarUI. On load it wires everything in the page: [data-pl-choice] groups, .pl-range sliders, [role=tablist] tabs, .pl-switch (adds role="switch") and [data-pl-sheet] buttons. Call PulsarUI.init(el) for content you add later.

FunctionDoes
toast(text, {action, onAction, icon, duration})A snackbar above the bottom edge. One at a time. Returns {close()}.
sheet.open(el, {onClose}) / sheet.close(el?)Slides a .pl-sheet up over a scrim. Focus stays inside; Esc, the scrim and [data-pl-close] close it.
menu(anchor, items, {label})A menu under the button: [{label, icon, danger, disabled, onSelect}, "-"]. Arrow keys, Esc, tap outside. Returns {close()}.
dialog({title, body, confirm, cancel, danger})Resolves true or false. cancel: null leaves only the confirm button.
theme("auto" | "light" | "dark")Sets the theme; with no argument returns it. Fires pl-theme on document.
icon(name, {size, label})An <svg> from the icon set.
init(root)Wires the controls inside root.
Events
import PulsarUI from "./ui/pulsar-ui.js";

// chips, segmented controls and card chips
document.getElementById("size").addEventListener("pl-change", (e) => save({ size: e.detail.value }));
// sliders: "input" while dragging, "pl-change" with a number when the finger lifts
document.getElementById("bright").addEventListener("pl-change", (e) => save({ bright: e.detail.value }));
// switches are checkboxes: use their own "change"
document.getElementById("ads").addEventListener("change", (e) => save({ ads: e.target.checked }));
// set a choice from code (no event)
document.getElementById("size").plSet("l");

Tokens

Every value is a CSS custom property you can use in your own styles. They are the app’s own (tokens.xml), light and dark.

--pl-ground#F7F7F8 · #171717Page
--pl-card#FFFFFF · #222222Cards, sheets
--pl-hair#E8E8E8 · #303030Borders
--pl-field#F4F4F4 · #262626Fields, tracks
--pl-ink#0D0D0D · #EDEDEDText, primary
--pl-mut#5E5E5E · #A8A8A8Second text
--pl-faint#9CA3AF · #8F8F8FHints
--pl-accent#10A37F · #10A37FSuccess, on
--pl-red#D93B3B · #F26B6BDanger
--pl-amber#B45309 · #F0B254Warning
Sizes and motion
--pl-gutter: 16px;   --pl-button: 48px;   --pl-touch: 48px;   --pl-icon: 22px;
--pl-radius-card: 20px;  --pl-radius-hero: 24px;  --pl-radius-sheet: 28px;
--pl-radius-tile: 16px;  --pl-radius-tile-small: 12px;  --pl-radius-menu: 16px;  --pl-menu-row: 48px;

--pl-short: 120ms;  --pl-medium: 200ms;  --pl-long: 280ms;  --pl-menu-in: 170ms;  --pl-menu-out: 110ms;
--pl-ease-standard: cubic-bezier(.4, 0, .2, 1);    /* moving */
--pl-ease-decelerate: cubic-bezier(0, 0, .2, 1);   /* entering */
--pl-ease-accelerate: cubic-bezier(.4, 0, 1, 1);   /* leaving */
--pl-ease-emphasized: cubic-bezier(.05, .7, .1, 1); /* sheets */

Typography

Inter, with the phone’s own font as the fallback. Titles 15 semibold, body 15 regular, small 13 medium.

Buttons

Black is the primary colour (white in dark mode). One primary button per screen. Heights 48, 40 and 34; small ones keep a 48 px touch area.

Switches and choices

The switch is the app’s own: a 40 × 24 track, an 18 px knob, 200 ms on the standard curve.

Fields

Pill fields on the field colour, 48 px high. The border shows on hover and focus.

Lists and setting rows

Rows of at least 56 px inside a card with a 20 px radius. Section titles above, footnotes below.

Cards, stats and media

Hairline borders, no shadows.

Feedback

Green only means done or on. Red only means danger. Amber warns. Blue informs.

Structure

Bars, tabs, dividers, avatars and icon tiles.

Menus, sheets and dialogs

Opened with pulsar-ui.js: focus stays inside, Esc and the scrim close them, focus goes back to the button.