https://github.com/thibaudcolas/kontrasto

🎨 Automated color contrast for text over images

https://github.com/thibaudcolas/kontrasto

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • β—‹
    CITATION.cff file
  • βœ“
    codemeta.json file
    Found codemeta.json file
  • β—‹
    .zenodo.json file
  • β—‹
    DOI references
  • β—‹
    Academic publication links
  • β—‹
    Committers with academic emails
  • β—‹
    Institutional organization owner
  • β—‹
    JOSS paper metadata
  • β—‹
    Scientific vocabulary similarity
    Low similarity (14.6%) to scientific vocabulary

Keywords

color-thief contrast-ratio django dominant-colors numpy pillow wagtail wcag2 wcag3
Last synced: 6 months ago · JSON representation

Repository

🎨 Automated color contrast for text over images

Basic Info
Statistics
  • Stars: 29
  • Watchers: 2
  • Forks: 3
  • Open Issues: 13
  • Releases: 0
Topics
color-thief contrast-ratio django dominant-colors numpy pillow wagtail wcag2 wcag3
Created almost 5 years ago · Last pushed about 2 years ago
Metadata Files
Readme Changelog Contributing License

README.md

Kontrasto

PyPI npm PyPI downloads Build status Coverage Status Total alerts Netlify Status

🎨 Automated color contrast for text over images

Why we need this

Kontrasto is a dual Python and JavaScript library which analyses instances of text over images, and transforms the text to make it more readable and have a higher contrast against the background.

Using Kontrasto both server-side and client-side gives the best results: server-side processing means users will have the best possible styles as the page loads, while client-side processing can refine the result based on the final position of the text over the image.

Here is a demo, over different areas of an image, with different methods of dominant color extraction and contrast ratio calculation:

Snow-covered landscape with snow-covered trees, blue-white sky, and a snow-covered radio tower on the horizon

Check out our live demo for other examples.

Usage in Python

kontrasto is available on PyPI.

```bash

Assuming you’re using Python 3.6+,

pip install kontrasto ```

The simplest way to try it out is to use the command-line interface:

bash kontrasto --text '#00ff00' demo_images/blue-sky-cliffs.jpg

This will extract the image’s dominant color, and compare it against three text colors: white, black, and the provided #00ff00. Here is a sample result:

  • Dominant color: #4971a1 (https://whocanuse.com/?b=4971a1&c=&f=16)
  • WCAG 2 contrast black: 4.16:1 (AA large only, https://whocanuse.com/?b=4971a1&c=000000&f=16)
  • WCAG 2 contrast white: 5.05:1 (AA, AAA large, https://whocanuse.com/?b=4971a1&c=ffffff&f=16)
  • WCAG 2 contrast text color: 3.68:1 (AA large only, https://whocanuse.com/?b=4971a1&c=00ff00&f=16)
  • WCAG 3 contrast black: 29.158302335633827
  • https://whocanuse.com/?b=4971a1&c=000000&f=16
  • WCAG 3 contrast white: 82.7306051896947
  • [(18, 400), (16, 500)]
  • https://www.myndex.com/APCA/?BG=4971a1&TXT=ffffff
  • https://whocanuse.com/?b=4971a1&c=ffffff&f=18
  • https://whocanuse.com/?b=4971a1&c=ffffff&f=16
  • WCAG 3 contrast text color: 60.98703767172198
  • [(24, 400), (18, 500), (16, 600)]
  • https://www.myndex.com/APCA/?BG=4971a1&TXT=00ff00
  • https://whocanuse.com/?b=4971a1&c=00ff00&f=24
  • https://whocanuse.com/?b=4971a1&c=00ff00&f=18
  • https://whocanuse.com/?b=4971a1&c=00ff00&f=16&s=b

From there, we can move onto more serious use cases!

Usage in vanilla Python

Import the low-level methods and enjoy:

```python from kontrasto import wcag2, wcag3 from kontrasto.convert import tohex from kontrasto.contrast import getdominant_color from PIL import Image

def wcag2contrastlightordark( image, lightcolor: str, darkcolor: str ) -> Dict[str, str]: dominant = tohex(getdominantcolor(image)) lightcontrast = wcag2.wcag2contrast(dominant, lightcolor) darkcontrast = wcag2.wcag2contrast(dominant, darkcolor) lighter = lightcontrast > darkcontrast return { "textcolor": lightcolor if lighter else darkcolor, "texttheme": "light" if lighter else "dark", "bgcolor": dominant, "bgtheme": "dark" if lighter else "light", }

def wcag3contrastlightordark( image, lightcolor: str, darkcolor: str ) -> Dict[str, str]: dominant = tohex(getdominantcolor(image)) lightcontrast = wcag3.formatcontrast( wcag3.apcacontrast(dominant, lightcolor) ) darkcontrast = wcag3.formatcontrast( wcag3.apcacontrast(dominant, darkcolor) ) lighter = lightcontrast > darkcontrast return { "textcolor": lightcolor if lighter else darkcolor, "texttheme": "light" if lighter else "dark", "bgcolor": dominant, "bgtheme": "dark" if lighter else "light", } ```

Usage in Wagtail

In Wagtail, using Kontrasto is much simpler:

  • The result of dominant color extraction is cached, greatly improving performance.
  • The above methods are directly available as template tags.

At least for now, this does mean using Kontrasto requires adding a field to a custom image model:

```python from wagtail.images.models import ( AbstractImage, AbstractRendition, SourceImageIOError, )

from kontrasto.willowoperations import pillowdominant

class CustomImage(AbstractImage): dominantcolor = models.CharField(maxlength=10, blank=True)

admin_form_fields = Image.admin_form_fields

def has_dominant_color(self) -> bool:
    return self.dominant_color

def set_dominant_color(self, color: str) -> bool:
    self.dominant_color = color

def get_dominant_color(self):
    if not self.has_dominant_color():
        with self.get_willow_image() as willow:
            try:
                self.dominant_color = pillow_dominant(willow)
            except Exception as e:
                # File not found
                #
                # Have to catch everything, because the exception
                # depends on the file subclass, and therefore the
                # storage being used.
                raise SourceImageIOError(str(e))

            self.save(update_fields=["dominant_color"])

    return self.dominant_color

class Rendition(AbstractRendition): image = models.ForeignKey( "CustomImage", relatedname="renditions", ondelete=models.CASCADE )

class Meta:
    unique_together = (("image", "filter_spec", "focal_point_key"),)

```

Then, in templates:

```html {% wcag2contrastlightordark page.testimage "#ffffff" "#000000" as result %} {% wcag3contrastlightordark page.testimage "#ffffff" "#000000" as result3 %} <div data-banner style="--kontrasto-text:{{ result3.textcolor }}; --kontrasto-bg:{{ result3.bg_color }}99;"

{% image page.testimage width-800 loading="lazy" data-banner-image="true" %}

{{ demotext }}

```

This additionally relies on the following CSS, for the simplest integration with client-side processing:

css .kontrasto-text-bg { color: var(--kontrasto-text); background: var(--kontrasto-bg); }

Usage in JavaScript

kontrasto is available on npm for client-side (browser) JavaScript. This option makes it possible to only extract the dominant color of images where the text appears, which leads to much better results. However, it has the caveat of executing in the users’ browser, which has a clear performance cost.

bash npm install kontrasto

Using it in JavaScript is slightly different.

Vanilla JavaScript

Here is a basic vanilla JavaScript integration:

```js import { wcag2contrastlightordark, wcag3contrastlightordark, } from "kontrasto";

const banner = document.querySelector("[data-banner]"); const bannerImage = banner.querySelector("[data-banner-image]"); const bannerText = banner.querySelector("[data-banner-text]");

const contrast = wcag3contrastlightordark( bannerImage, "#ffffff", "#000000", bannerText, ); banner.style.setProperty("--kontrasto-bg", `${contrast.bgcolor}99); banner.style.setProperty("--kontrasto-text", contrast.text_color); ``

This assumes an HTML structure like:

```html {% wcag2contrastlightordark page.testimage "#ffffff" "#000000" as result %} {% wcag3contrastlightordark page.testimage "#ffffff" "#000000" as result3 %} <div data-banner style="--kontrasto-text:{{ result3.textcolor }}; --kontrasto-bg:{{ result3.bg_color }}99;"

{% image page.testimage width-800 loading="lazy" data-banner-image="true" %}

{{ demotext }}

```

And CSS:

css .kontrasto-text-bg { color: var(--kontrasto-text); background: var(--kontrasto-bg); }

Combined with the server-side integration, this makes it possible to deliver both the best possible performance, and the best possible text contrast enhancement.

Particular considerations

TODO

React

TODO

Contributing

See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our contribution guidelines.

If you just want to set up the project on your own computer, the contribution guidelines also contain all of the setup commands.

Credits

Image credit: FxEmojis. Test templates extracted from third-party projects. Website hosted by Netlify.

View the full list of contributors. MIT licensed. Website content available as CC0.

Owner

  • Name: Thibaud Colas
  • Login: thibaudcolas
  • Kind: user
  • Location: Cambridge, UK
  • Company: @Torchbox

President @django, core team @wagtail, building things @torchbox. Accessibility & climate action – /tee-bo/

GitHub Events

Total
  • Watch event: 1
Last Year
  • Watch event: 1

Committers

Last synced: 10 months ago

All Time
  • Total Commits: 38
  • Total Committers: 2
  • Avg Commits per committer: 19.0
  • Development Distribution Score (DDS): 0.184
Past Year
  • Commits: 0
  • Committers: 0
  • Avg Commits per committer: 0.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
Thibaud Colas t****s@g****m 31
Renovate Bot b****t@r****m 7
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 3
  • Total pull requests: 24
  • Average time to close issues: 5 days
  • Average time to close pull requests: 12 months
  • Total issue authors: 3
  • Total pull request authors: 3
  • Average comments per issue: 0.67
  • Average comments per pull request: 0.33
  • Merged pull requests: 3
  • Bot issues: 1
  • Bot pull requests: 22
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • p4535992 (1)
  • Myndex (1)
  • renovate[bot] (1)
Pull Request Authors
  • renovate[bot] (23)
  • thibaudcolas (1)
  • nimasmi (1)
Top Labels
Issue Labels
bug (1) question (1)
Pull Request Labels
enhancement (23)

Packages

  • Total packages: 2
  • Total downloads:
    • pypi 26 last-month
    • npm 2 last-month
  • Total dependent packages: 1
    (may contain duplicates)
  • Total dependent repositories: 3
    (may contain duplicates)
  • Total versions: 2
  • Total maintainers: 1
npmjs.org: kontrasto

🎨 Automated color contrast for text over images

  • Versions: 1
  • Dependent Packages: 1
  • Dependent Repositories: 1
  • Downloads: 2 Last month
Rankings
Stargazers count: 7.8%
Forks count: 9.1%
Dependent repos count: 10.8%
Dependent packages count: 21.4%
Average: 22.6%
Downloads: 63.7%
Maintainers (1)
Last synced: 6 months ago
pypi.org: kontrasto

🎨 Automated color contrast for text over images

  • Versions: 1
  • Dependent Packages: 0
  • Dependent Repositories: 2
  • Downloads: 26 Last month
Rankings
Dependent packages count: 7.4%
Dependent repos count: 11.9%
Stargazers count: 12.9%
Forks count: 17.0%
Average: 25.0%
Downloads: 75.6%
Maintainers (1)
Last synced: 6 months ago

Dependencies

requirements.txt pypi
setup.py pypi
.github/workflows/ci.yml actions
.github/workflows/planttrees.yml actions
package-lock.json npm
package.json npm
pyproject.toml pypi