Framework
codedgar/Puppertino avatar
codedgar/Puppertino

Puppertino is plain CSS that copies the macOS and iOS component vocabulary

A CSS framework based on Human Guidelines from apple

1,198 stars49 forksMDXMIT

At a glance

What is it?
Puppertino is a CSS framework that follows Apple's Human Interface Guidelines, with no build step and no dependencies, shipped as three bundles and a set of optional vanilla JavaScript helpers. The 2.0 release keeps every version 1 class working, which makes it a low risk drop in, but the desktop and mobile component sets are separate, so you choose which platform you are building for.
Who is it for?
Puppertino fits a team that wants a Mac looking interface in the browser without a component library dependency, and it is a safe upgrade from version 1 because the old class names still work. It is the wrong choice if you need a single component set that covers both desktop and mobile behaviours, since the desktop and mobile bundles are shipped separately.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly MDX, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

No build step and no dependencies is the pitch

Puppertino describes itself as a CSS framework that follows Apple's Human Interface Guidelines, with components for both pointer and touch, and the project's own framing is that it brings the look of macOS and iOS to the web. The most important technical fact is the second sentence of the description: it is plain CSS, there is no build step, and there are no dependencies. Some components come with a small vanilla JavaScript helper, and each one is optional.

That constraint shapes everything else. A framework with a build step gives you a toolchain to install, a compile step to run, and a version of the framework to keep in step with your bundler. Puppertino gives you a stylesheet and a set of class names. The cost is that the framework cannot preprocess, cannot use Sass variables, and cannot tree shake itself, so the browser downloads what the bundle contains.

The repository layout supports the plain reading of the project. There is a dist directory with the stylesheets, including the generated.min.css bundles, and a src directory with the JavaScript helpers, a docs-src directory that is an Astro project for the documentation site, and a.stylelintrc.json for linting the stylesheets. The package main and style fields both point at dist/css/puppertino.css, and the files array ships only dist, src, the README and the licence.

It is worth noting the version history. Version 1.0 was released on 2021-04-26 and version 2.0.0, named Puppertino Dachshund, on 2026-10-06. That is a long gap between the two, which means the version 1 class names have had years to be used and are worth preserving carefully.

Three bundles, and picking the wrong one costs you a platform

The install section offers three bundles, and they are not simply a size choice. Each includes the foundations, which are colors, typography, materials and dark mode. The Full bundle, puppertino.min.css, contains every component. The Desktop bundle, puppertino-desktop.min.css, is the macOS component set. The Mobile bundle, puppertino-mobile.min.css, is the iOS and iPadOS component set.

So the choice is a platform decision. A page that ships the desktop bundle gets buttons, forms, segmented controls, sliders, disclosure controls, color wells, image wells, date pickers, menus, popovers, tooltips, modals, dialogs and sheets, notifications, windows, sidebars, toolbars, the menu bar and the Dock, lists and tables, progress indicators and scrollbars. The mobile bundle instead gives you action sheets, tab bars, navigation bars, lists with swipe actions and empty states.

The consequence is that a responsive site which wants both has to load both, or load the full bundle, and the two component sets are not the same set of class names. That is a real constraint for anyone whose design has to work on a desktop browser and a phone.

The CDN directory is a single folder, and the README asks you to pin the version in the URL, explaining that a pinned link keeps rendering the same way when a new release ships. That advice is not a detail: this is a stylesheet that replaces the visual language of a page, so an unpinned URL turns a routine dependency bump into a redesign.

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@codedgar/[email protected]/dist/css/puppertino.min.css">

That one link is the whole quick start. You add it to the head of the page, then write markup with the p- prefixed classes.

The foundations are the part you inherit whether you want them or not

Every bundle, including the smallest one, includes the foundations, and that is where the framework's opinionated part lives.

Colors come as thirteen system accents, six text levels, five fills and semantic backgrounds, each with light and dark values. Typography covers Apple's text styles from Large Title down to Caption 2, with weights and tracking. Materials give five thicknesses of blur and translucency, elevation shadows, and Liquid Glass. Layout provides size classes, split views, containers and a readable width.

The accent count is the number that matters if you have a brand colour. Thirteen system accents is the Apple palette, and a project that needs a specific brand hue either accepts a non standard value outside the scale or overrides the token. The README does not document a public customisation API for the accent set, so a team with a strict brand guideline will be editing CSS variables or class names rather than configuring a theme.

That is the honest trade in a framework that is explicitly following another company's design system. You get a coherent set of components that look like the platform for free, and you take on the assumption that your product should look like macOS or iOS.

The npm entry points mirror the same granularity as the bundles, plus per component paths, so an application that only needs buttons and a colour palette does not need the whole framework.

javascript
// Everything
import '@codedgar/puppertino'

// One platform
import '@codedgar/puppertino/desktop'
import '@codedgar/puppertino/mobile'

// One component, plus the colors it reads from
import '@codedgar/puppertino/colors'
import '@codedgar/puppertino/buttons'

The package exports map lists each of these paths explicitly, and the README notes that the same paths work with an @import in CSS.

Dark mode is three classes and an optional script

The dark mode mechanism is small and worth understanding, because it is scoped rather than global. There are three classes. p-auto-dark-mode follows the system appearance, p-dark-mode forces dark, and p-light-mode pins a region to light inside a dark page. The class goes on the body element or on any element to scope it, which means a light island inside a dark page is a matter of putting p-light-mode on a container rather than a special build of the stylesheet.

For a manual toggle that remembers the choice and syncs across tabs, the framework ships darkmode_manager.js as an optional helper, and the README gives the call explicitly.

html
<script src="https://cdn.jsdelivr.net/npm/@codedgar/[email protected]/src/js/darkmode_manager.js"></script>
<script>
  puppertinoThemeMan.init({ autoDetect: true });
</script>

The same pattern applies to every interactive component. The CSS works without JavaScript, and the components that open, float or drag have a helper in src/js/: menus, popovers, modals, sheets, sliders, date pickers and the Dock among them. Each component page in the documentation names the helper it needs, so you import only what you use.

javascript
import '@codedgar/puppertino/js/menus.js'
import '@codedgar/puppertino/js/modals.js'

That optionality is the difference between a CSS framework and a component library, and it is the reason the framework can claim no dependencies. It also means a modal without its helper will render but not behave, which is the kind of thing that only shows up in a demo that never opened a real dialog.

Accessibility is delegated to the browser rather than implemented

The accessibility section is short, and its content reveals the approach. Puppertino responds to the system accessibility settings: reduced motion turns off animated movement, reduced transparency swaps blurred materials for opaque surfaces, increased contrast strengthens borders and fills, and keyboard focus stays visible.

None of that is a bespoke implementation. Each of those is a CSS media query against a system preference that the browser already exposes, and honouring them is a matter of writing the styles so the preference takes effect. The interesting part is that the framework treats the preferences as part of the design rather than as an afterthought, so a user with reduced transparency gets a readable page instead of a page with unreadable text on a blurred panel.

There is one explicit fallback. Materials fall back to an opaque surface in browsers without backdrop-filter, which matters because the entire materials layer, the five thicknesses of blur and translucency plus Liquid Glass, depends on that property. On a browser without it, the framework degrades to solid surfaces rather than to an unreadable page.

What the README does not document is how the framework behaves in that fallback, for example whether the elevation shadows and borders are adjusted to keep the same visual hierarchy once the translucency is gone. If your product depends on the material look, that is worth checking in the browsers you support rather than assuming.

Version 2 keeps every version 1 class alive, and the docs ship as plain text

The migration story is the strongest argument for the 2.0 release, and it is unusually careful. Every version 1 class still works, because the version 2 stylesheets include the version 1 classes alongside the new ones, so upgrading does not require changes to your markup. Pages that link newfull.css keep working as well, and that file is frozen as the complete version 1 stylesheet and will not change. For new work the README points you at puppertino.css or puppertino.min.css instead.

One rename is scheduled for the future rather than done now. The dark mode script was renamed from dakmode_manager.js to darkmode_manager.js, and the old name still loads but will be removed in a future major version. That is the only deprecation listed, and it is scheduled rather than silent.

The other unusual decision is publishing the documentation as plain text for automated tools. The docs are generated from the same sources as the site, and two files are published: llms.txt as an index of every page, and llms-full.txt as the full documentation in one file with an index of every class. For a framework whose entire vocabulary is class names, a flat list of those class names is a reasonable thing to hand to a code generating tool.

The project is MIT licensed with the text in the LICENSE file, it is not archived, and the last push was on 2026-10-13, a week after the 2.0.0 tag. The contributing guide notes that the CSS in dist/css is written by hand and should be edited directly, while the three .min.css bundles are generated by the build.

bash
npm install
npm run build:css

The documentation site is a separate Astro project in docs-src with its own install and dev script, and the built site is checked into the docs directory.

Editorial conclusion

Puppertino fits a team that wants a Mac looking interface in the browser without a component library dependency, and it is a safe upgrade from version 1 because the old class names still work. It is the wrong choice if you need a single component set that covers both desktop and mobile behaviours, since the desktop and mobile bundles are shipped separately. Verify first that the backdrop-filter fallback looks acceptable in the browsers you support, and pin the version in the stylesheet URL so a future release cannot change your rendering.

Frequently asked questions

How do I add Puppertino to a page?

Add one stylesheet to the head of the page, for example the link tag pointing at https://cdn.jsdelivr.net/npm/@codedgar/[email protected]/dist/css/puppertino.min.css, then use the p- prefixed classes in your markup. There is no build step and no dependency to install.

What is the difference between the Puppertino bundles?

Three bundles ship and each includes the foundations. The Full bundle contains every component, the Desktop bundle is the macOS component set, and the Mobile bundle is the iOS and iPadOS component set, so a site that needs both platform behaviours has to load both or the full bundle.

Does Puppertino need JavaScript?

No. The CSS works without JavaScript, and the components that open, float or drag have an optional small vanilla JavaScript helper in src/js, with menus, popovers, modals, sheets, sliders, date pickers and the Dock among them. Each component page in the docs names the helper it needs.

Can I upgrade from Puppertino version 1 without changing my markup?

Yes. Every version 1 class still works because the version 2 stylesheets include the version 1 classes, and newfull.css is frozen as the complete version 1 stylesheet. The dark mode script was renamed from dakmode_manager.js to darkmode_manager.js, with the old name still loading for now.

How does Puppertino handle users who ask for reduced transparency?

The framework responds to system accessibility settings, so reduced motion turns off animated movement, reduced transparency swaps blurred materials for opaque surfaces and increased contrast strengthens borders and fills. Materials also fall back to an opaque surface in browsers without backdrop-filter.

Official sources

  1. codedgar/Puppertino on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/codedgar-puppertino.svg)](https://hysenlabs.com/projects/codedgar-puppertino)

Was this useful?