The bare minimum for elegant interfaces.

Three classes, no framework,3.2 kB.

A CSS and JavaScript library with no reset, no font, no runtime dependencies and no rule outside a pui- class; the browser does the work.

index.html3 / 3
<button class="pui-btn pui-solid pui-theme">Button</button>
shapestylecolour

Three classes, no framework,3.2 kB.

3.2 kB. Measured, not promised.

perfectui.css is 3,221 bytes gzip; the optional JavaScript loader is 493. The bars below are the same measurement run on the published builds of the alternatives.

CSSJavaScript
perfectui1.0.0-beta.1
3,7143,221 + 493
Pico2.1.1
11,640
Beer CSS5.0.3
22,89917,035 + 5,864
Bootstrap5.3.8
54,61230,869 + 23,743
UIkit3.25.24
84,26130,944 + 53,317
Bulma1.0.4
64,842

gzip -9 -n of the published builds; perfectui measured at build from version 1.0.0-beta.1, others on 2026-09-23

A shape, a style, a colour.

Every element is up to three classes you can combine freely: what it is, how the colour is applied, which colour. Seven colours and four styles give 28 looks per shape, and adding a colour never multiplies your classes.

shape
style
colour
<button class="pui-btn pui-solid pui-theme">
themesuccesserrorwarnmutedsurfaceinversesolid
soft
outline
link

Overlays without a plugin.

Modals are <dialog>, menus are popover, tooltips are interestfor, accordions are <details>. The optional loader downloads a fallback only when a browser is missing one of them.

Modal<dialog>no JavaScript of yours
Delete project
This cannot be undone.
<button
  class="pui-btn pui-solid pui-theme"
  commandfor="confirm"
  command="show-modal"
>
  Delete project
</button>

<dialog class="pui-modal" id="confirm" closedby="any">
  <div class="pui-card">
    <div class="pui-card-header">Delete project</div>
    <div class="pui-card-content">
      <span>This cannot be undone.</span>
      <div class="pui-group-row">
        <button
          class="pui-btn pui-outline pui-surface"
          commandfor="confirm"
          command="close"
        >
          Cancel
        </button>
        <button
          class="pui-btn pui-solid pui-error"
          commandfor="confirm"
          command="close"
        >
          Delete
        </button>
      </div>
    </div>
  </div>
</dialog>
Dropdownpopoverno JavaScript of yours
<button class="pui-btn pui-outline pui-surface" popovertarget="menu">
  Menu
</button>

<div class="pui-dropdown" id="menu" popover>
  <ul class="pui-list pui-hoverable" style="list-style: none">
    <li class="pui-list-item">Profile</li>
    <li class="pui-list-item">Billing</li>
    <li class="pui-list-item">Sign out</li>
  </ul>
</div>
Tooltipinterestforno JavaScript of yours
We never share your email.
<button class="pui-btn pui-outline pui-surface" interestfor="help">?</button>

<div class="pui-tooltip" id="help" popover="hint">
  We never share your email.
</div>
Accordion<details>no JavaScript of yours
What is Perfect UI?

A lightweight CSS and JavaScript library.

Does it need JavaScript?

Only to emulate what a browser is missing.

<div class="pui-accordion">
  <details class="pui-accordion-item">
    <summary>What is Perfect UI?</summary>
    <p>A lightweight CSS and JavaScript library.</p>
  </details>
  <details class="pui-accordion-item">
    <summary>Does it need JavaScript?</summary>
    <p>Only to emulate what a browser is missing.</p>
  </details>
</div>

One attribute for dark mode. One variable for your colour.

With no attribute, perfectui follows the operating system. Set data-pui-mode to choose, and --pui-theme to make every component yours.

<html data-pui-mode="light">
--pui-theme: #7c3aed;
Dark mode docs
New project
Your trial ends in 3 daysAdd a payment method to keep your projects.
Ana Souza$120.00Paid
Bruno Lima$80.00Pending
Carla Dias$45.00Failed
OperationalDegradedVerified

Your utilities always win.

One @layer line keeps Tailwind's reset below perfectui and every utility above it.

class="pui-btn pui-solid pui-theme w-full"
@layer utilities.w-full wins
@layer componentsyours
@layer pui.pui-btn …
@layer basePreflight
@layer themetokens
@layer theme, base, pui, components, utilities;
@custom-variant dark (&:where([data-pui-mode="dark"], [data-pui-mode="dark"] *));

Nothing you have to undo.

No reset to override,no font to remove,no !important to beat,no initialiser to call after rendering,no guard for server rendering.ESM only, nothing on window.

Coming from 0.x?

1.0 is a rewrite: every class is prefixed, styles and colours are separate classes, and the browser replaced the JavaScript. The guide lists every change.

button.html
- btn style-solid-primary
+ pui-btn pui-solid pui-theme
Read the migration guide

Install it your way.

A package for npm, yarn, pnpm or bun, or two tags from a CDN. Import everything or only the components you use.

Get started
$
import "@chrissgon/perfectui/perfectui.css";
// or only what you use:
import "@chrissgon/perfectui/core.css";
import "@chrissgon/perfectui/components/button.css";
CDN
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@chrissgon/perfectui@1.0.0-beta.1/dist/perfectui.css">
<script type="module">
  import "https://cdn.jsdelivr.net/npm/@chrissgon/perfectui@1.0.0-beta.1/dist/js/index.js";
</script>