Skip to content

Commit be7ff70

Browse files
committed
sarvam shri krishna sankalpam
0 parents  commit be7ff70

File tree

10 files changed

+252
-0
lines changed

10 files changed

+252
-0
lines changed

.color_pairs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
min_contrast: AA
2+
pairs:
3+
- background: '#ffffff'
4+
foreground: '#000000'
5+
- background: '#000000'
6+
foreground: '#ffffff'

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v3
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: '3.10'
20+
21+
- name: Install dependencies
22+
run: |
23+
python -m pip install --upgrade pip
24+
pip install .
25+
26+
- name: Run Lint
27+
run: |
28+
# Create a sample config for testing
29+
cc-lint init
30+
# Run lint
31+
cc-lint lint

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Color Contrast Linter
2+
3+
A CLI tool to lint color pairs for contrast compliance using `cm-colors`.
4+
5+
## Installation
6+
7+
```bash
8+
pip install color-contrast-linter
9+
```
10+
11+
## Usage
12+
13+
### Initialize Configuration
14+
15+
Run the `init` command to create a `.color_pairs.yml` file:
16+
17+
```bash
18+
cc-lint init
19+
```
20+
21+
This creates a configuration file where you can define your color pairs and the minimum contrast standard (AA or AAA).
22+
23+
```yaml
24+
min_contrast: AA
25+
pairs:
26+
- foreground: "#000000"
27+
background: "#ffffff"
28+
- foreground: "#ffffff"
29+
background: "#000000"
30+
```
31+
32+
### Lint Colors
33+
34+
Run the `lint` command to check your color pairs:
35+
36+
```bash
37+
cc-lint lint
38+
```
39+
40+
The tool supports Hex, RGB, and RGBA color formats.
41+
42+
## CI/CD Integration
43+
44+
You can use this tool in your CI/CD pipelines to ensure accessibility compliance.
45+
46+
```yaml
47+
steps:
48+
- uses: actions/checkout@v3
49+
- name: Set up Python
50+
uses: actions/setup-python@v4
51+
with:
52+
python-version: '3.x'
53+
- name: Install dependencies
54+
run: pip install color-contrast-linter
55+
- name: Lint colors
56+
run: cc-lint lint
57+
```

pyproject.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "color-contrast-linter"
7+
version = "0.1.0"
8+
description = "A CLI tool to lint color pairs for contrast compliance using cm-colors."
9+
readme = "README.md"
10+
requires-python = ">=3.8"
11+
dependencies = [
12+
"cm-colors>=0.5.0",
13+
"click",
14+
"rich",
15+
"pyyaml",
16+
]
17+
18+
[project.scripts]
19+
cc-lint = "color_contrast_linter.cli:main"

src/color_contrast_linter/__init__.py

Whitespace-only changes.
191 Bytes
Binary file not shown.
4.5 KB
Binary file not shown.
3.04 KB
Binary file not shown.

src/color_contrast_linter/cli.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import click
2+
from rich.console import Console
3+
from rich.table import Table
4+
from pathlib import Path
5+
import sys
6+
7+
from cm_colors import Color, ColorPair
8+
from .config import load_config, create_default_config, CONFIG_FILE_NAME
9+
10+
console = Console()
11+
12+
@click.group()
13+
def main():
14+
"""A CLI tool to lint color pairs for contrast compliance."""
15+
pass
16+
17+
@main.command()
18+
def init():
19+
"""Initialize the .color_pairs.yml configuration file."""
20+
config_path = Path(CONFIG_FILE_NAME)
21+
if config_path.exists():
22+
console.print(f"[yellow]Warning:[/yellow] {CONFIG_FILE_NAME} already exists.")
23+
if not click.confirm("Do you want to overwrite it?"):
24+
return
25+
26+
create_default_config(config_path)
27+
console.print(f"[green]Success:[/green] Created {CONFIG_FILE_NAME} with sample data.")
28+
console.print("You can now edit this file to add your own color pairs.")
29+
30+
@main.command()
31+
def lint():
32+
"""Lint color pairs defined in .color_pairs.yml."""
33+
config = load_config()
34+
if not config:
35+
console.print(f"[red]Error:[/red] Could not find or parse {CONFIG_FILE_NAME}.")
36+
console.print(f"Run [bold]cc-lint init[/bold] to create one.")
37+
sys.exit(1)
38+
39+
table = Table(title="Color Contrast Lint Results")
40+
table.add_column("Foreground", style="cyan")
41+
table.add_column("Background", style="magenta")
42+
table.add_column("Status", justify="center")
43+
44+
failed_count = 0
45+
standard = config.min_contrast.upper()
46+
console.print(f"Checking against standard: [bold]{standard}[/bold]")
47+
48+
for pair_config in config.pairs:
49+
try:
50+
pair = ColorPair(pair_config.foreground, pair_config.background)
51+
52+
readability = pair.is_readable
53+
54+
passed = False
55+
if standard == "AAA":
56+
passed = readability == "Very Readable"
57+
else: # Default to AA
58+
passed = readability in ["Readable", "Very Readable"]
59+
60+
status = "[green]PASS[/green]" if passed else "[red]FAIL[/red]"
61+
if not passed:
62+
failed_count += 1
63+
64+
table.add_row(
65+
pair_config.foreground,
66+
pair_config.background,
67+
status
68+
)
69+
70+
except Exception as e:
71+
console.print(f"[red]Error processing pair {pair_config.foreground} on {pair_config.background}: {e}[/red]")
72+
failed_count += 1
73+
74+
console.print(table)
75+
76+
if failed_count > 0:
77+
console.print(f"\n[red]Lint failed with {failed_count} errors.[/red]")
78+
sys.exit(1)
79+
else:
80+
console.print("\n[green]All color pairs passed contrast checks![/green]")
81+
82+
if __name__ == "__main__":
83+
main()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import yaml
2+
from pathlib import Path
3+
from typing import List, Dict, Optional
4+
from dataclasses import dataclass
5+
6+
CONFIG_FILE_NAME = ".color_pairs.yml"
7+
8+
@dataclass
9+
class ColorPairConfig:
10+
foreground: str
11+
background: str
12+
13+
@dataclass
14+
class Config:
15+
min_contrast: str
16+
pairs: List[ColorPairConfig]
17+
18+
def load_config(path: Path = Path(CONFIG_FILE_NAME)) -> Optional[Config]:
19+
if not path.exists():
20+
return None
21+
22+
with open(path, "r") as f:
23+
try:
24+
data = yaml.safe_load(f)
25+
except yaml.YAMLError:
26+
return None
27+
28+
if not data:
29+
return None
30+
31+
min_contrast = data.get("min_contrast", "AA")
32+
pairs = []
33+
for item in data.get("pairs", []):
34+
pairs.append(ColorPairConfig(
35+
foreground=item.get("foreground", ""),
36+
background=item.get("background", "")
37+
))
38+
39+
return Config(min_contrast=min_contrast, pairs=pairs)
40+
41+
def create_default_config(path: Path = Path(CONFIG_FILE_NAME)):
42+
default_data = {
43+
"min_contrast": "AA",
44+
"pairs": [
45+
{
46+
"foreground": "#000000",
47+
"background": "#ffffff"
48+
},
49+
{
50+
"foreground": "#ffffff",
51+
"background": "#000000"
52+
}
53+
]
54+
}
55+
with open(path, "w") as f:
56+
yaml.dump(default_data, f, default_flow_style=False)

0 commit comments

Comments
 (0)