diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6783312 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,13 @@ +exclude: ^assets/fontawesome/ + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-added-large-files + - id: check-merge-conflict + - id: mixed-line-ending diff --git a/CNAME b/CNAME index 94e1fef..e71d0fc 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -python-pillow.org \ No newline at end of file +python-pillow.org diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc1a056 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Pillow Website + +This website is hosted at [python-pillow.org](https://python-pillow.org/) +(also reachable at [python-pillow.github.io](https://python-pillow.github.io/)). + +It's a static site — plain HTML, CSS, and a touch of vanilla JS, with no build +step or framework. GitHub Pages serves it directly from the `main` branch. + +## Structure + +- `index.html` — the entire site (single page) +- `assets/css/pillow.css` — all styles +- `assets/js/back-to-top.js` — the "back to top" button behavior +- `assets/images/` — logos, hero art, and other images +- `assets/fontawesome/` — vendored Font Awesome icons + +## Local preview + +Since there's no build step, any static file server works, for example: + +``` +python3 -m http.server 8000 +``` + +Then open http://localhost:8000/ in a browser. + +## Contributing + +This repo uses [pre-commit](https://pre-commit.com/) for basic hygiene +checks (trailing whitespace, end-of-file, YAML/JSON validation, etc.). Install +it and run against all files with: + +``` +pre-commit run --all-files +``` + +or, if you have [just](https://github.com/casey/just) installed: + +``` +just precommit +``` diff --git a/README.rst b/README.rst deleted file mode 100644 index 7ff35d4..0000000 --- a/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -Pillow Website -============== - -This website is live at `python-pillow.github.io `_ diff --git a/assets/css/pillow.css b/assets/css/pillow.css index 458dc7a..b4bbccf 100644 --- a/assets/css/pillow.css +++ b/assets/css/pillow.css @@ -1,8 +1,763 @@ -.bg-pillow { - background-image: url(../images/img49.png); - background-size: 100% 100%; +/* ========================================================================== + Pillow — custom site styles (no CSS framework) + + Palette rationale: + - Coral/terracotta and magenta are pulled directly from the flowers in + the Pillow logo photo. + - Sky blue is pulled from the desert/sky photo in the same logo. + - All three also appear in assets/images/psychedelic-glow.jpg, the + psychedelic rainbow texture from the original PIL-era site (art by + Jeremy Kun), which we reuse as a subtle glow behind the hero logo for + a bit of the site's history and visual uniqueness. Pre-blurred and + pre-saturated at build time (rather than via CSS `filter`) to avoid + the runtime GPU cost of filtering a full-viewport layer. + - Ink (near-black) and warm paper (off-white) anchor everything so the + brighter accent colors stay legible and don't overwhelm the page. + ========================================================================== */ + +:root { + --ink: #17181c; + --ink-soft: #4b4d55; + --paper: #faf7f2; + --paper-dim: #efe9df; + --coral: #e8703a; + --coral-dark: #c85a29; + --magenta: #c23b74; + --sky: #3e7ea6; + --sky-rgb: 62, 126, 166; + --line: rgba(23, 24, 28, .12); + --max: 1200px; +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} + +body { + margin: 0; + font-family: "Inter", "Segoe UI", system-ui, sans-serif; + background-color: var(--paper); + color: var(--ink); + line-height: 1.6; +} + +h1, h2, h3, h4, .brand-mark { + font-family: "Space Grotesk", "Segoe UI", system-ui, sans-serif; + letter-spacing: -.02em; + line-height: 1.15; +} + +a { + color: var(--coral-dark); +} + +a:hover { + color: var(--magenta); +} + +img { + max-width: 100%; + display: block; +} + +.wrap { + max-width: var(--max); + margin-inline: auto; + padding-inline: 1.5rem; +} + +/* ---------------------------------------------------------------- Navbar */ + +.site-header { + background-color: var(--ink); + border-bottom: 3px solid var(--coral); +} + +.site-header .wrap { + display: flex; + align-items: center; + justify-content: space-between; + padding-block: .6rem; +} + +.brand { + display: flex; + align-items: center; +} + +.brand img { + height: 112px; + width: auto; +} + +.header-actions { + display: flex; + align-items: center; + gap: 1.25rem; +} + +.icon-link { + color: rgba(250, 247, 242, .75); + font-size: 1.15rem; + transition: color .15s ease-in-out; +} + +.icon-link:hover { + color: var(--paper); +} + +.site-nav { + display: flex; + align-items: center; + gap: 1.5rem; + margin-right: .5rem; + padding-right: 1.5rem; + border-right: 1px solid rgba(250, 247, 242, .15); +} + +.site-nav a { + color: rgba(250, 247, 242, .7); + font-family: "Space Grotesk", sans-serif; + font-size: .9rem; + font-weight: 600; + text-decoration: none; + transition: color .15s ease-in-out; +} + +.site-nav a:hover { + color: var(--paper); +} + +@media (max-width: 900px) { + .site-nav { + display: none; + } +} + +/* ---------------------------------------------------------------- Buttons */ + +.btn { + display: inline-flex; + align-items: center; + gap: .5rem; + font-family: "Space Grotesk", sans-serif; + font-weight: 600; + font-size: .95rem; + padding: .7rem 1.4rem; + border-radius: .3rem; + border: 2px solid transparent; + text-decoration: none; + cursor: pointer; + transition: background-color .15s ease-in-out, color .15s ease-in-out, border-color .15s ease-in-out; +} + +.btn-lg { + padding: .85rem 1.5rem; + font-size: 1.05rem; +} + +.btn-sm { + padding: .4rem 1rem; + font-size: .85rem; +} + +.btn-primary { + background-color: var(--coral); + border-color: var(--coral); + color: #fff; +} + +.btn-primary:hover { + background-color: var(--coral-dark); + border-color: var(--coral-dark); + color: #fff; +} + +.btn-outline { + background-color: transparent; + border-color: rgba(250, 247, 242, .5); + color: var(--paper); +} + +.btn-outline:hover { + border-color: var(--paper); + background-color: rgba(250, 247, 242, .08); + color: var(--paper); +} + +.btn-sponsor { + background-color: var(--magenta); + border-color: var(--magenta); + color: #fff; +} + +.btn-sponsor:hover { + background-color: #a52f60; + border-color: #a52f60; + color: #fff; +} + +.btn-sponsor-light { + background-color: var(--paper); + border-color: var(--paper); + color: var(--magenta); +} + +.btn-sponsor-light:hover { + background-color: transparent; + border-color: var(--paper); + color: var(--paper); +} + +.btn-ink { + background-color: transparent; + border-color: var(--ink); + color: var(--ink); +} + +.btn-ink:hover { + background-color: var(--ink); + border-color: var(--ink); + color: var(--paper); +} + +/* ------------------------------------------------------------------ Hero */ + +.hero { + position: relative; + color: var(--paper); + overflow: hidden; + isolation: isolate; +} + +.hero::before { + /* Psychedelic art by Jeremy Kun, from the original PIL-era site. + Blur/saturation are pre-baked into the image itself (see the + palette rationale note above), so no CSS filter is needed here. */ + content: ""; + position: absolute; + inset: 0; + background-image: url(../images/psychedelic-glow.jpg); + background-size: cover; + background-position: center; + opacity: .45; + mix-blend-mode: screen; + z-index: -2; +} + +.hero::after { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient(circle at 10% 15%, rgba(232, 112, 58, .25), transparent 45%), + radial-gradient(circle at 90% 85%, rgba(var(--sky-rgb), .22), transparent 45%), + var(--ink); + opacity: .7; + z-index: -1; +} + +.hero .wrap { + display: grid; + grid-template-columns: 1.3fr .7fr; + align-items: center; + gap: 2.5rem; + padding-block: 5rem; +} + +.hero-eyebrow { + text-transform: uppercase; + letter-spacing: .14em; + font-size: .8rem; + font-weight: 700; + color: var(--coral); + margin: 0 0 .75rem; +} + +.hero h1 { + font-size: clamp(2.5rem, 5vw, 3.75rem); + font-weight: 700; + margin: 0 0 1.25rem; +} + +.hero p { + color: rgba(250, 247, 242, .78); + font-size: 1.15rem; + max-width: 42ch; +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: .75rem; + margin-top: 2rem; +} + +.hero-art { + justify-self: center; +} + +.hero-art img { + display: block; + width: 100%; + height: auto; + max-width: 320px; +} + +@media (max-width: 980px) { + .hero .wrap { + grid-template-columns: 1fr; + text-align: center; + padding-block: 3.5rem; + } + + .hero p { + max-width: none; + margin-inline: auto; + } + + .hero-actions { + justify-content: center; + } + + .hero-art { + order: -1; + } + + .hero-art img { + display: block; + max-width: 200px; + margin-inline: auto; + } +} + +@media (max-width: 600px) { + .hero-actions { + flex-direction: column; + align-items: stretch; + } + + .hero-actions .btn { + text-align: center; + justify-content: center; + } +} + +/* ---------------------------------------------------------- Section base */ + +section { + padding-block: 4rem; +} + +#history { + padding-bottom: 1.5rem; +} + +.section-head { + max-width: 56ch; + margin-bottom: 2.5rem; +} + +.section-head.center { + margin-inline: auto; + text-align: center; +} + +.section-head h2 { + font-size: 2rem; + margin: 0 0 .75rem; +} + +.section-head h2 .highlight { + color: var(--coral); +} + +.section-head p { + color: var(--ink-soft); + font-size: 1.1rem; + margin: 0; +} + +/* ------------------------------------------------------------- History */ + +.timeline { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 0; + border-top: 1px solid var(--line); +} + +.timeline-item { + padding: 1.75rem 1.5rem 2rem; + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); +} + +.timeline-item:first-child { + padding-left: 0; +} + +.timeline-item:last-child { + border-right: none; + padding-right: 0; +} + +.timeline-item .index { + font-family: "Space Grotesk", sans-serif; + font-weight: 700; + font-size: 1.25rem; + letter-spacing: -.02em; + color: var(--coral); + margin-bottom: .5rem; + display: block; +} + +.timeline-item p { + margin: 0; + color: var(--ink-soft); +} + +@media (max-width: 860px) { + .timeline { + grid-template-columns: 1fr; + } + + .timeline-item { + padding: 1.25rem 0; + border-right: none; + border-bottom: 1px solid var(--line); + } + + .timeline-item:last-child { + border-bottom: none; + } +} + +/* ------------------------------------------------------- Sponsor section */ + +.sponsor { + background: linear-gradient(135deg, var(--magenta), #6a3fa0); + color: #fff; +} + +.sponsor .section-head p { + color: rgba(255, 255, 255, .85); +} + +.sponsor-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin: 2rem 0 2.5rem; +} + +.sponsor-chip { + background-color: rgba(255, 255, 255, .1); + border: 1px solid rgba(255, 255, 255, .25); + border-radius: .4rem; + padding: 1.1rem 1.25rem; + font-size: .98rem; +} + +.sponsor-chip i { + margin-right: .5rem; +} + +.sponsor-cta { + text-align: center; +} + +@media (max-width: 760px) { + .sponsor-grid { + grid-template-columns: 1fr; + } +} + +/* ------------------------------------------------------------ In memory */ + +.memory { + padding: .5rem 1.5rem 2rem; + text-align: center; + border-bottom: 1px solid var(--line); +} + +.memory a { + color: var(--ink); + text-decoration: none; + font-family: "Space Grotesk", sans-serif; + font-size: 1.2rem; + font-weight: 600; + border-bottom: 2px solid var(--coral); + padding-bottom: .1rem; +} + +.memory-note { + max-width: 52ch; + margin: .9rem auto 0; + color: var(--ink-soft); + font-size: .98rem; +} + +/* --------------------------------------------------------------- Lists */ + +.info-list { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0; + border-top: 1px solid var(--line); +} + +.info-item { + padding: 2rem 1.75rem; + border-bottom: 1px solid var(--line); + border-right: 1px solid var(--line); +} + +.info-item:last-child { + border-right: none; +} + +.info-item h3 { + font-size: 1.25rem; + margin: 0 0 .5rem; +} + +.info-item p { + margin: 0; + color: var(--ink-soft); +} + +@media (max-width: 860px) { + .info-list { + grid-template-columns: 1fr; + } + + .info-item { + border-right: none; + } +} + +/* -------------------------------------------------------------- Case studies */ + +.case-studies { + background-color: var(--paper-dim); +} + +.case-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1px; + background-color: var(--line); + border: 1px solid var(--line); +} + +.case-item { + background-color: var(--paper-dim); + padding: 1.75rem; +} + +.case-item h3 { + font-size: 1.1rem; + margin: 0 0 .4rem; + display: flex; + align-items: center; + gap: .5rem; +} + +.case-item h3 i { + color: var(--coral-dark); +} + +.case-item p { + margin: 0; + color: var(--ink-soft); +} + +@media (max-width: 760px) { + .case-grid { + grid-template-columns: 1fr; + } +} + +/* ----------------------------------------------------------- Enterprise */ + +.enterprise { + text-align: center; +} + +.enterprise img.sonar { + max-width: 220px; + margin: 1.5rem auto; +} + +.enterprise p { + color: var(--ink-soft); + max-width: 60ch; + margin-inline: auto; +} + +/* --------------------------------------------------------------- Footer */ + +.site-footer { + background-color: var(--ink); + border-top: 3px solid var(--coral); + color: rgba(250, 247, 242, .7); + font-size: .92rem; +} + +.footer-grid { + display: grid; + grid-template-columns: 1.4fr 1fr 1fr 1.2fr; + gap: 2.5rem; + padding-block: 3rem 2rem; +} + +.footer-brand .brand img { + height: 44px; + width: auto; +} + +.footer-brand p { + margin-block: 1rem 0; + max-width: 26ch; + line-height: 1.5; +} + +.footer-social { + display: flex; + gap: 1.1rem; + margin-top: 1.1rem; +} + +.footer-social .icon-link { + font-size: 1.2rem; +} + +.footer-col h4 { + color: var(--paper); + font-size: .8rem; + text-transform: uppercase; + letter-spacing: .08em; + margin-bottom: 1rem; +} + +.footer-col ul { + list-style: none; + display: grid; + gap: .6rem; + margin: 0; + padding: 0; +} + +.footer-col a, +.footer-social .icon-link { + color: rgba(250, 247, 242, .7); + transition: color .15s ease-in-out; +} + +.footer-col a:hover, +.footer-social .icon-link:hover { + color: var(--coral); +} + +.footer-sponsor h4 { + display: flex; + align-items: center; + gap: .4rem; +} + +.footer-sponsor h4 i { + color: var(--coral); +} + +.footer-sponsor p { + line-height: 1.5; + margin-bottom: 1.1rem; +} + +.footer-bottom { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + flex-wrap: wrap; + border-top: 1px solid rgba(250, 247, 242, .12); + padding-block: 1.25rem; + font-size: .82rem; +} + +.footer-bottom p { + margin: 0; +} + +.footer-bottom a { + color: rgba(250, 247, 242, .7); + white-space: nowrap; +} + +.footer-bottom a:hover { + color: var(--coral); +} + +@media (max-width: 860px) { + .footer-grid { + grid-template-columns: 1fr 1fr; + } + + .footer-brand { + grid-column: 1 / -1; + } +} + +@media (max-width: 520px) { + .footer-grid { + grid-template-columns: 1fr; + } +} + +/* ---------------------------------------------------------- Back to top */ + +.back-to-top { + position: fixed; + right: 1.5rem; + bottom: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + width: 3rem; + height: 3rem; + border-radius: 50%; + background-color: var(--ink); + color: var(--paper); + border: 2px solid var(--coral); + box-shadow: 0 6px 16px rgba(0, 0, 0, .25); + text-decoration: none; + font-size: 1.1rem; + z-index: 10; + opacity: 0; + visibility: hidden; + transform: translateY(.5rem); + pointer-events: none; + transition: opacity .2s ease-in-out, transform .2s ease-in-out, background-color .15s ease-in-out, visibility .2s; +} + +.back-to-top.is-visible { + opacity: 1; + visibility: visible; + transform: translateY(0); + pointer-events: auto; } -#top-info a { - color: #F8F9FA; +.back-to-top:hover { + background-color: var(--coral-dark); + transform: translateY(-2px); } diff --git a/assets/images/Tidelift_primary-logo.png b/assets/images/Tidelift_primary-logo.png deleted file mode 100644 index f5c79d0..0000000 Binary files a/assets/images/Tidelift_primary-logo.png and /dev/null differ diff --git a/assets/images/img49.png b/assets/images/img49.png deleted file mode 100644 index 21fd732..0000000 Binary files a/assets/images/img49.png and /dev/null differ diff --git a/assets/images/pillow-logo-light-text-480x240.png b/assets/images/pillow-logo-light-text-480x240.png new file mode 100644 index 0000000..58b4b23 Binary files /dev/null and b/assets/images/pillow-logo-light-text-480x240.png differ diff --git a/assets/images/psychedelic-glow.jpg b/assets/images/psychedelic-glow.jpg new file mode 100644 index 0000000..03b835e Binary files /dev/null and b/assets/images/psychedelic-glow.jpg differ diff --git a/assets/images/sonar-logo.svg b/assets/images/sonar-logo.svg new file mode 100644 index 0000000..00fd315 --- /dev/null +++ b/assets/images/sonar-logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/js/back-to-top.js b/assets/js/back-to-top.js new file mode 100644 index 0000000..6251b73 --- /dev/null +++ b/assets/js/back-to-top.js @@ -0,0 +1,30 @@ +// Show the "back to top" button only once the user has scrolled down, +// or when they reach the bottom of the page. +(function () { + var button = document.querySelector(".back-to-top"); + if (!button) return; + + var SHOW_AFTER_PX = 600; + // Small fudge factor so sub-pixel scroll rounding still counts as "at + // the bottom" of the page. + var BOTTOM_FUDGE_PX = 4; + var ticking = false; + + function update() { + var scrollY = window.scrollY || window.pageYOffset; + var pageHeight = document.documentElement.scrollHeight; + var isScrollable = pageHeight > window.innerHeight; + var atBottom = isScrollable && window.innerHeight + scrollY >= pageHeight - BOTTOM_FUDGE_PX; + button.classList.toggle("is-visible", scrollY > SHOW_AFTER_PX || atBottom); + ticking = false; + } + + window.addEventListener("scroll", function () { + if (!ticking) { + window.requestAnimationFrame(update); + ticking = true; + } + }, { passive: true }); + + update(); +})(); diff --git a/index.html b/index.html index 8119773..abf2824 100644 --- a/index.html +++ b/index.html @@ -4,160 +4,310 @@ + content="Pillow: the Python Imaging Library. Adds image processing capabilities to your Python interpreter."> - - + content="Fredrik Lundh, Jeffrey 'Alex' Clark, Pillow Contributors"> - + + + + + Python Pillow - + - + +
-
-
-
-
- Pillow logo + +
+
+
+

Python Imaging Library

+

Pillow

+

Pillow adds powerful image processing capabilities to your Python + interpreter — open, edit, and save many different image file formats + with a simple, well-documented API.

+ +
+
+ Pillow logo +
+
+
+ + +
+
+
+

Project

+
+
+
+

Documentation

+

+ Our documentation is hosted by Read the Docs and includes + installation instructions, + handbook, + API reference and + release notes. +

-

Pillow

-
-

This is the home of Pillow, the friendly PIL fork.

-

PIL is an acronym for Python Imaging Library. - If you have ever worried or wondered about the future of PIL, please stop. We're here to save the day.

+
+

Discussion

+

+ Discussion about Pillow development, programming and technical issues occurs on + GitHub, + Stack Overflow and + Gitter. +

-
- Get from PyPI - Read the docs +
+

Source Code

+

+ Our source code is hosted by GitHub and tested on + GitHub Actions and + Codecov, and uploaded to the + Python Package Index. +

-
+ + + + + +
+
+

For Enterprise

+ Sonar logo +

Available as part of the Sonar Subscription.

+

+ The maintainers of Pillow and thousands of other packages are working with Sonar + (formerly Tidelift) to deliver commercial support and maintenance for the open source + dependencies you use to build your applications. Save time, reduce risk, and improve + code health, while paying the maintainers of the exact dependencies you use. +

+ + Learn More +
+
+ + +
+
+
+

Powering the Python ecosystem

+

+ Pillow is a dependency of thousands of packages. Here are a few well-known open + source projects that rely on it for image processing. +

-
-
-
-

Discussion

-

- Discussion about Pillow development, programming and technical issues occurs on - GitHub, - Stack Overflow and - Gitter. -

-
+
+
+

Django

+

+ The Django web + framework uses Pillow to validate and introspect uploaded images for its + built-in ImageField. +

-
-
-
-
-

Source Code

-

- Our source code is hosted by GitHub and tested on - GitHub Actions, - AppVeyor, - Travis CI, - Codecov and uploaded to the - Python Package Index. -

-
+
+

Wagtail

+

+ Wagtail, the popular + Django-based CMS, depends on Pillow to generate the image renditions and + thumbnails used throughout its admin and templates. +

+
+
+

Home Assistant

+

+ Home Assistant + uses Pillow to process camera snapshots and generate thumbnails across its + smart home integrations. +

+
+
+

Matplotlib

+

+ Matplotlib, the widely + used plotting library, uses Pillow to read and write image formats such as + JPEG when saving figures. +

-
-
-

For Enterprise

- Tidelift logo -

Available as part of the Tidelift Subscription.

-

- The maintainers of Pillow and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. -

-

- - Free Trial +

+ + +
+
+
+

From PIL to Pillow

+

+ Pillow is a fork of the Python Imaging Library (PIL), but after more than a + decade of active development it has effectively become the Python Imaging Library.

+
+
+ 1995 +

Fredrik Lundh, aka Effbot, releases the Python Imaging Library (PIL).

+
+
+ 2010 +

Jeffrey ‘Alex’ Clark forks + the project and renames it to Pillow to add setuptools support. A community of + contributors soon joins in.

+
+
+ 2013 +

Development of the original PIL has stopped, with no releases since + 2009. Pillow is adopted as its replacement in Linux distributions + including Debian and Ubuntu.

+
+
+ 2020 +

Following the PEP 541 + package abandonment process, PyPI moderators + declare the original PIL package officially abandoned, + confirming Pillow as its de facto successor.

+
+
+ Today +

Pillow is maintained by a global community of contributors and is a + dependency of thousands of Python projects worldwide.

+
+
+
+ + +
+ In memory of Fredrik Lundh +

+ Pillow 9.0.0 was dedicated to the memory of Fredrik Lundh, aka Effbot, who died in + November 2021. Fredrik created PIL in 1995 and was instrumental in the + early success of Python. +

-