Metadata-Version: 2.5
Name: slabkit
Version: 0.1.1
Summary: SVG panel system for GitHub profile READMEs — OKLCH palettes with asserted contrast, superellipse corners, one motion idea per panel.
Project-URL: Homepage, https://github.com/hellosverre/slab
Project-URL: Source, https://github.com/hellosverre/slab
Project-URL: Issues, https://github.com/hellosverre/slab/issues
Author-email: Sverre <sverresig@proton.me>
License-Expression: MIT
License-File: LICENSE
Keywords: design-system,generative,github,oklch,profile,readme,svg
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Software Development :: Documentation
Requires-Python: >=3.9
Provides-Extra: fonts
Requires-Dist: fonttools>=4.0; extra == 'fonts'
Description-Content-Type: text/markdown

<div align="center">
  <img alt="slab — SVG panels for GitHub READMEs" width="100%"
       src="https://raw.githubusercontent.com/hellosverre/slab/main/assets/hero.svg">
</div>

<div align="center">
  <a href="https://pypi.org/project/slabkit/"><img alt="PyPI" src="https://img.shields.io/pypi/v/slabkit?style=flat-square&color=76D5A1&labelColor=081A10&label=pypi"></a>
  &nbsp;
  <img alt="Python" src="https://img.shields.io/badge/python-3.9%2B-081A10?style=flat-square&logo=python&logoColor=76D5A1&labelColor=081A10">
  &nbsp;
  <img alt="No runtime dependencies" src="https://img.shields.io/badge/runtime%20deps-none-081A10?style=flat-square&labelColor=081A10">
  &nbsp;
  <img alt="MIT" src="https://img.shields.io/badge/licence-MIT-76D5A1?style=flat-square&labelColor=081A10">
</div>

<br>

A small Python library that generates the panels on a GitHub profile: a hero banner, a
stack grid, a status strip, a system diagram, a contact footer. Everything comes out as a
self-contained animated SVG with no external requests.

Every image above and below was generated by this library. If it could not document
itself it would not be worth publishing.

## Why this exists

GitHub is a hostile rendering target, and most of the rules only announce themselves once
something silently breaks:

| Constraint | Consequence |
|---|---|
| `<style>` is escaped to plain text | No CSS. No `:target`, no `:hover`, no CSS-only toggles. |
| JavaScript is stripped | Nothing is interactive. |
| Images are proxied through camo | **Webfonts never load.** A `font-family` pointing at one silently falls back. |
| SVG is served as `<img>` | Pointer events never reach it, so links inside an SVG are dead. |
| `id` is rewritten to `user-content-id` | Anchor tricks lose their handle. |

What *does* survive is SMIL animation, filters, masks, gradients, and system fonts. slab
is built entirely inside that envelope, which is why it uses `<animate>` rather than CSS
keyframes and outlines display type to vector paths rather than linking a font.

## Install

```bash
pip install slabkit
```

The distribution is `slabkit` because `slab` was already taken on PyPI by a DSP library,
and sharing an import name with it would break anyone who has both installed.

No runtime dependencies — the whole library is string formatting and arithmetic.
`fonttools` is an optional extra, needed only for `outline()`, which converts a wordmark
to paths so a non-system typeface survives camo:

```bash
pip install "slabkit[fonts]"
```

## Quickstart

```python
from slabkit import Theme, banner, chips, footer, write

t = Theme(158)                      # one hue derives the entire ramp

write("banner.svg", banner(
    t, "SVERRE", "building AI systems, fundamentals first",
    meta="TypeScript · Python · Next.js · Postgres",
    note="Ski · Norway"))

write("stack.svg", chips(t, [
    ("WRITE", ["TypeScript", "Python"]),
    ("RUN",   ["Linux", "Docker"]),
]))
```

Reference the result from your README by its `raw.githubusercontent.com` URL. Relative
paths are risky on profile READMEs, which render outside the repo's base path.

## Theming

`Theme(hue)` derives ink, surface, text, muted, dim and accent in OKLCH from a single hue,
varying almost entirely in lightness. It then **asserts the contrast ratios and raises if
any role falls below 4.5:1**, so an unreadable palette fails at build time rather than
shipping.

<div align="center">
  <img alt="The same ramp at five hues" width="100%"
       src="https://raw.githubusercontent.com/hellosverre/slab/main/assets/hues.svg">
</div>

```python
Theme(158).report()     # audit any hue before committing to it
Theme(250, accent="#81C3FF")    # override individual roles when you must
```

## Panels

#### `banner(theme, wordmark, tagline, meta, note, font=...)`

Pass `font=` a `.ttf`/`.otf` to outline the wordmark to paths. Any typeface works, because
the glyphs ship as geometry rather than as a font reference.

#### `chips(theme, groups)`

<img alt="chips panel" width="100%" src="https://raw.githubusercontent.com/hellosverre/slab/main/assets/panel-chips.svg">

A raking light crosses the grid, brightening each chip as it passes. Two copies of the
grid, the lit one revealed through a soft gradient **mask** — a clip would give a hard
edge, which reads as a wipe rather than as light.

#### `status(theme, label, state, word, headline, detail, right, stamp)`

<img alt="status panel" width="100%" src="https://raw.githubusercontent.com/hellosverre/slab/main/assets/panel-status.svg">

`state` is `ok` / `warn` / `bad`. Those colours are semantic rather than brand, which is
why they are exempt from the one-accent rule — but they are derived at the accent's OKLCH
lightness so they still belong to the same band.

**Always pass `stamp`.** A committed SVG behind camo is stale by design, and a status
panel implying real-time is simply lying.

#### `diagram(theme, title, subtitle, nodes, edges, caption)`

<img alt="diagram panel" width="100%" src="https://raw.githubusercontent.com/hellosverre/slab/main/assets/panel-diagram.svg">

Nodes are dicts, edges are index pairs. A request relays along the edges and responses
return, staggered by graph depth so parallel branches fire together instead of in series.
Useful when the code is private and the shape is the only thing you can show.

#### `footer(theme, headline, sub, right)` · `pill(theme, label, on)`

<img alt="footer panel" width="100%" src="https://raw.githubusercontent.com/hellosverre/slab/main/assets/panel-footer.svg">

`pill()` renders one small standalone pill — a language toggle, nav, a tag. One image per
pill on purpose: markdown hangs a single `href` off an `<img>`, so a combined toggle would
send both halves to the same place.

## Design rules

These are the constraints the panels are built to, and they are the actual product. Most
generated READMEs look identical because they all make the same handful of colour
decisions; each rule below exists to refuse one of them.

- **One accent.** Hierarchy comes from lightness, not chroma. `Theme` keeps the accent
  under 80% HSL saturation.
- **Never the default dark.** Slate-indigo ink around `#0c0e15` is the night-mode twin of
  the blue-to-purple gradient. Pick a hue and commit to it.
- **No gradient-clipped headline text.** Solid ink; weight and size do the work.
- **No coloured glow, no glow blobs.** Depth is one hairline border and a rim light.
- **Grain on every surface.** A perfectly smooth gradient is the giveaway that nothing
  physical made it.
- **Superellipse corners, not `border-radius`.** A radius corner has discontinuous
  curvature where the arc meets the edge; a superellipse does not.
- **One motion idea per panel**, and stagger the timings across panels. Elements pulsing
  in unison is what makes motion look cheap.

## Releasing

Publishing runs on [Trusted Publishing](https://docs.pypi.org/trusted-publishers/), so
there is no API token anywhere in this repo — GitHub mints a short-lived credential for
the `publish` workflow and PyPI verifies it.

One-time setup on PyPI (Account → Publishing → add a pending publisher):

| Field | Value |
|---|---|
| PyPI project name | `slabkit` |
| Owner | `hellosverre` |
| Repository name | `slab` |
| Workflow name | `publish.yml` |
| Environment name | `pypi` |

After that, bump `version` in `pyproject.toml` and publish a GitHub release. The workflow
builds, runs `twine check`, installs the wheel into a clean venv and renders every panel
before it uploads anything.

## Licence

MIT. See [LICENSE](LICENSE).
