Skip to main content

Pixel-craft PySide6/QML component kit — macOS first

Project description

pearl-kit

Pixel-craft PySide6/QML component kit. macOS first.

PyPI Python License CI

A curated set of QML primitives built on QtQuick.Templates, sourced from the shadcn/ui design language, with a three-theme token system and no framework abstractions.


What it is

pearl-kit is a small, opinionated QML component library for PySide6 desktop apps that care how they look on macOS. Instead of fighting Qt's default widget styling — or settling for Material Design — it treats components as a curated kit: a coherent token system, a handful of carefully-built primitives, shadcn-level attention to detail, and zero framework abstractions.

It's not trying to be the next big Python UI framework. It's trying to be the missing layer between PySide6 and a real-looking 2026 desktop app.

Why it exists

Python has had the "beautiful desktop app" problem for a decade. PyQt and PySide6 are powerful but default-ugly; the community has largely routed around them with Electron sidecars, webviews, and Flutter bridges. The recurring complaint in every "Python desktop UI in 2025" thread is some variant of "everything looks ten years out of date, and every Tailwind-level fix takes 1,000 lines of QSS."

pearl-kit is the answer that assumes:

  • You want to stay in Python/QML for real reasons — NumPy, SciPy, PyTorch, domain libraries, or just because your backend is already there.
  • You want the app to look like Linear or Raycast, not like a 2012 Qt demo.
  • You don't want another framework — you want pixels you can drop in.

QML is the right substrate for this. It's GPU-accelerated, declarative, animation-first, and officially native on macOS since Qt 6. pearl-kit brings the shadcn/Linear design sensibility on top of it.

Install

pip install pearl-kit

or with uv:

uv add pearl-kit

Requirements: Python 3.11+, PySide6 6.8+, macOS or Linux.

Quick look

A sign-in card with three shipped components — Button, Input, Toggle — and one typography primitive (PearlText):

import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

import pearl_kit

app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
pearl_kit.register_qml(engine)

engine.loadData(b"""
import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import PearlKit 1.0 as P

Window {
    width: 480; height: 360; visible: true
    color: P.Tokens.background

    ColumnLayout {
        anchors.centerIn: parent
        spacing: P.Tokens.space.x4
        width: 280

        P.Input {
            Layout.fillWidth: true
            placeholderText: "you@domain.com"
        }

        RowLayout {
            Layout.fillWidth: true
            spacing: P.Tokens.space.x2

            P.Toggle { checked: true }
            P.PearlText {
                text: "Remember me"
                variant: "body"
            }
            Item { Layout.fillWidth: true }
        }

        P.Button {
            Layout.fillWidth: true
            text: "Sign in"
            variant: "default"
        }
    }
}
""")

app.exec()

register_qml() adds the bundled QML directory to your engine's import path, so import PearlKit 1.0 resolves from anywhere.

Components

Eight primitives ship in v0.1.0. Each one is built on QtQuick.Templates.* with a full visual override — no reliance on Qt's default widget styling.

Component What it does API surface Docs
Button Clickable action 6 variants × 5 sizes, iconLeft/iconRight, loading, checkable
Input Single-line text field error state, iconLeft/iconRight, password mode
Toggle On/off switch 2 sizes, animated thumb, keyboard focus ring
Select Non-editable dropdown 2 sizes, error state, model-driven, grouped items
Dialog Modal popover title/description, footer slot, close button, enter/exit transitions
CheckBox Multi-select box error state, indeterminate (tristate)
Stepper Numeric field with ± buttons int + float modes, suffix, specialValueText, error state
PearlText Typography primitive 7 variants — title, heading, body, muted, label, code, mono

Run python examples/gallery.py after a uv sync to see every variant live.

The token system

Three themes ship out of the box: Dark, DarkBlue, Light (default). Switch at runtime — every binding re-evaluates automatically:

P.Tokens.mode = P.Tokens.Light

Color tokens follow the shadcn/ui naming convention:

background   foreground       card         cardForeground
popover      popoverForeground primary      primaryForeground
secondary    secondaryForeground accent     accentForeground
muted        mutedForeground   destructive destructiveForeground
success      warning           border      input        ring
elevation0   elevation1        elevation2  elevation3

Scale tokens:

Category API Values
Radius Tokens.radius.{sm,md,lg,xl,full} 4, 6, 8, 12, 9999
Spacing Tokens.space.x{0..12} 4-based scale: 0, 4, 8, 12, 16, 20, 24, 32, 40, 48
Typography Tokens.font.size.{xs..xxl} 12, 14, 16, 18, 20, 24
Font Tokens.font.ui / .mono "SF Pro Display" / "SF Mono"
Shadow Tokens.shadow.{sm,md,lg}{1,2} Two-layer shadcn-style
Motion Tokens.motion.{fast,base,slow} 150, 180, 260 ms

Full reference: docs/tokens.

Roadmap

Shipped — v0.1.0

The first component tier, each one built on QtQuick.Templates with full visual override:

  • Button — 6 variants × 5 sizes, icon slots, loading, checkable
  • Input — single-line field with error state and icon slots
  • Toggle — iOS-style switch, 2 sizes
  • Select — non-editable dropdown with grouped items and keyboard navigation
  • Dialog — modal popover with overlay, focus trap, and fade + zoom transitions
  • CheckBox — indeterminate-capable, error-aware
  • Stepper — numeric field with int + float modes and auto-repeat buttons
  • PearlText — typography primitive covering title through mono variants

Next — layout primitives

  • GroupBox — titled section with optional collapse
  • FormLayout / FormRow — label↔input alignment with hint and error slots
  • ScrollArea — thin-scrollbar wrapper matching the token set
  • Splitter — horizontal + vertical split with persistable sizes
  • Card — elevated surface with radius, border, padding
  • Separator — thin divider

Later

Feedback and navigation primitives — Slider, ProgressBar, Tooltip, Toast, Tabs, Menu, StatusBar, ListView, Badge, Avatar, CodeBlock, TextArea, and Spinner.

The scope is deliberately narrow. This is a component kit, not a framework — no reactivity engine, no router, no state management. Those belong in your app or in Qt Quick itself.

Philosophy

Three rules:

  1. macOS first, everything else follows. Tested on retina, SF Pro native, system conventions respected.
  2. Pixels over abstractions. No magic. Every component is one .qml file you can read in under ten minutes.
  3. Boring primitives, uncompromising execution. You should recognize every component from ten other design systems. What's different is the detail — the shadow layers, the focus rings, the transition curves.

Development

git clone https://github.com/omerdduran/pearl-kit
cd pearl-kit
uv sync --all-extras

uv run pytest              # full suite (55 tests today)
uv run ruff check .
uv run pyright              # strict mode
uv run mkdocs serve         # live docs preview

Releases are fully automated. Pushing a v* tag triggers a GitHub Actions workflow that builds, publishes to PyPI via trusted publishing, and creates a GitHub Release with wheel + sdist + sigstore attestations.

Links

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pearl_kit-0.2.0.tar.gz (69.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pearl_kit-0.2.0-py3-none-any.whl (31.1 kB view details)

Uploaded Python 3

File details

Details for the file pearl_kit-0.2.0.tar.gz.

File metadata

  • Download URL: pearl_kit-0.2.0.tar.gz
  • Upload date:
  • Size: 69.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pearl_kit-0.2.0.tar.gz
Algorithm Hash digest
SHA256 34cb021626bd8eea4a26ae34df9d0e98b0a8469e0b271cf6c3b602b497b6e242
MD5 b3b350b0d71826b7fcedbc47a2399e54
BLAKE2b-256 ac73738f9c7c8712e23ac896f14b7938d08d603fe3a1e128bc5e1b543f803dbb

See more details on using hashes here.

Provenance

The following attestation bundles were made for pearl_kit-0.2.0.tar.gz:

Publisher: release.yml on omerdduran/pearl-kit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pearl_kit-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pearl_kit-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 31.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pearl_kit-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 65c542f957083d9b0c74103fae3bc9048b2ba3a118521269b07d6bb2efeb1dc2
MD5 80e9f39165c759905f5832720403e133
BLAKE2b-256 2caca63b2ee123d515b4ea5b22675f8b0ecb620608e1f6111bec219c1a9f328b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pearl_kit-0.2.0-py3-none-any.whl:

Publisher: release.yml on omerdduran/pearl-kit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page