Css - biały

This commit is contained in:
dm
2025-12-19 14:56:02 +01:00
parent 8d984c7a9c
commit 1ec16fb089
7 changed files with 171 additions and 42 deletions

120
find_unused_css_classes.py Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
import argparse
import os
import re
from pathlib import Path
from typing import Dict, List, Set, Tuple
# --- Regexy do zbierania klas z CSS ---
# Łapie ".class-name" w selektorach (pomija np. ".5" i rzeczy z escapami prosto, ale działa w praktyce)
CSS_CLASS_RE = re.compile(r'(?<![\\\w-])\.([a-zA-Z_-][\w-]*)')
# --- Regexy do zbierania stringów klas z Astro/JSX/TSX ---
# 1) class="..."
CLASS_ATTR_RE = re.compile(r'\bclass\s*=\s*("([^"]*)"|\'([^\']*)\')', re.IGNORECASE)
# 2) className="..."
CLASSNAME_ATTR_RE = re.compile(r'\bclassName\s*=\s*("([^"]*)"|\'([^\']*)\')', re.IGNORECASE)
# Astro: class:list={{ a: cond, "b c": cond2 }} / class:list={[...]} tu łapiemy stringi w środku
STRING_LIT_RE = re.compile(r'("([^"]+)"|\'([^\']+)\')')
# Dla wyszukiwania tokenów klas (żeby "f-card" nie matchowało jako fragment "f-card-x")
def token_pattern(cls: str) -> re.Pattern:
return re.compile(r'(?<![\w-])' + re.escape(cls) + r'(?![\w-])')
def read_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8", errors="ignore")
except Exception:
return ""
def collect_css_classes(css_dir: Path) -> Tuple[Set[str], Dict[str, Set[Path]]]:
classes: Set[str] = set()
where: Dict[str, Set[Path]] = {}
for p in css_dir.rglob("*.css"):
txt = read_text(p)
for m in CSS_CLASS_RE.finditer(txt):
c = m.group(1)
classes.add(c)
where.setdefault(c, set()).add(p)
return classes, where
def collect_used_classes(code_dir: Path, candidates: Set[str]) -> Tuple[Set[str], Dict[str, Set[Path]]]:
used: Set[str] = set()
used_where: Dict[str, Set[Path]] = {c: set() for c in candidates}
exts = {".astro", ".jsx", ".tsx", ".js", ".ts"}
# prekompilacja patternów dla szybkości
patterns = {c: token_pattern(c) for c in candidates}
for p in code_dir.rglob("*"):
if not p.is_file():
continue
if p.suffix.lower() not in exts:
continue
txt = read_text(p)
if not txt:
continue
# szybki filtr: jeśli żaden kandydat nie ma nawet prefiksu "f-" / "jmb-" itd,
# to i tak skan tokenowy jest ok, ale tu robimy prosty scan wszystkich.
for c, pat in patterns.items():
if pat.search(txt):
used.add(c)
used_where[c].add(p)
# usuń puste wpisy
used_where = {k: v for k, v in used_where.items() if v}
return used, used_where
def main():
ap = argparse.ArgumentParser(description="Znajdź potencjalnie nieużywane klasy CSS w projekcie Astro/JSX.")
ap.add_argument("--css", required=True, help="Katalog z plikami CSS (np. src/styles)")
ap.add_argument("--code", required=True, help="Katalog z kodem (np. src)")
ap.add_argument("--min-len", type=int, default=3, help="Minimalna długość nazwy klasy (domyślnie 3)")
ap.add_argument("--prefix", action="append", default=[], help="Filtruj klasy po prefiksie (np. --prefix f- --prefix jmb-)")
ap.add_argument("--show-where", action="store_true", help="Pokaż gdzie zdefiniowano klasę w CSS")
args = ap.parse_args()
css_dir = Path(args.css).resolve()
code_dir = Path(args.code).resolve()
if not css_dir.exists():
raise SystemExit(f"Brak katalogu CSS: {css_dir}")
if not code_dir.exists():
raise SystemExit(f"Brak katalogu code: {code_dir}")
all_classes, defined_where = collect_css_classes(css_dir)
# filtr długości + prefiksów
classes = {c for c in all_classes if len(c) >= args.min_len}
if args.prefix:
classes = {c for c in classes if any(c.startswith(px) for px in args.prefix)}
used, used_where = collect_used_classes(code_dir, classes)
unused = sorted(classes - used)
print(f"CSS katalog: {css_dir}")
print(f"CODE katalog: {code_dir}")
print(f"Klasy w CSS: {len(classes)} (po filtrach)")
print(f"Użyte w kodzie:{len(used)}")
print(f"NIEUŻYTE: {len(unused)}")
print("-" * 60)
for c in unused:
print(c)
if args.show_where:
files = sorted(defined_where.get(c, []))
for f in files:
rel = f.relative_to(css_dir.parent) if css_dir.parent in f.parents else f
print(f" defined in: {rel}")
print("-" * 60)
# opcjonalnie: pokaż top kilka użyć
# (jak chcesz, dopiszę flagę na raport "gdzie użyte")
if __name__ == "__main__":
main()

View File

@@ -53,7 +53,7 @@
}
.f-card-label {
@apply text-base font-medium opacity-80;
@apply text-base font-medium opacity-95;
}
.f-card-value {

View File

@@ -8,7 +8,7 @@
}
p:first-of-type {
@apply py-3 text-3xl text-gray-400;
@apply py-3 text-3xl;
}
}

View File

@@ -110,7 +110,8 @@ margin: 1.75rem auto;
border-radius: 2rem;
border: 1px solid color-mix(in oklab, var(--f-border-color) 82%, transparent 18%);
background: color-mix(in oklab, var(--f-background) 96%, black 10%);
background-color: var(--f-premium-bg);
/* background: color-mix(in oklab, var(--f-background) 96%, black 10%); */
box-shadow:
0 18px 40px -28px rgba(0,0,0,.35),

View File

@@ -6,6 +6,10 @@
@apply text-2xl;
}
.fuz-markdown p a:hover{
@apply text-[--f-link-text-hover];
}
.fuz-markdown h1 {
@apply text-3xl font-bold mt-8 mb-4;
}

View File

@@ -1,46 +1,45 @@
/* @layer components { */
.f-section-header {
@apply text-4xl md:text-5xl font-bold mb-3 text-[--f-header];
.f-section-header {
@apply text-4xl md:text-5xl font-bold mb-3 text-[--f-header];
}
}
.f-section {
@apply pt-1 pb-1 mx-2 my-6;;
}
.f-section {
@apply pt-1 pb-1 mx-2 my-6;
;
}
.f-section-center {
@apply f-section text-center;
}
.f-section-center {
@apply f-section text-center;
}
.f-section-grid {
@apply grid items-center gap-5 max-w-7xl mx-auto mt-8;
}
.f-section-grid {
@apply grid items-center gap-5 max-w-7xl mx-auto mt-8;
}
.f-section-grid-single {
@apply grid items-center gap-5 max-w-7xl mx-auto;
}
.f-section-grid-single {
@apply grid items-center gap-5 max-w-7xl mx-auto;
}
.f-section-grid-single-center {
@apply f-section-grid-single text-center;
}
.f-section-grid-single-center {
@apply f-section-grid-single text-center;
}
.f-section-image {
@apply w-full object-contain;
}
.f-section-image {
@apply w-full object-contain;
}
.f-image-dimmed {
@apply opacity-[1];
}
.f-image-dimmed {
@apply opacity-[1];
}
.f-section-title {
@apply text-4xl md:text-5xl font-bold mb-2 text-[--f-header];
}
.f-section-title {
@apply text-4xl md:text-5xl font-bold mb-2 text-[--f-header];
}
.f-section-nav {
@apply mt-0 flex justify-center;
}
.f-section-nav {
@apply mt-0 flex justify-center;
}
/* } */

View File

@@ -7,27 +7,30 @@
--brand-dark: hsl(200 calc(100% / 0.2) calc(50% / 1.5));
--f-navbar-height: 84px;
/* -------------------------- */
--f-hero-text: #d1d6d9;
--f-hero-header: #d1d6d9;
/* --- Background and Text --- */
--f-background: #e0e6eb;
--f-background: #ffffff;
/* #e0e6eb; */
--f-text: #36525e;
--f-header: #001a33;
--f-header-items: (#001a33);
/*--- Navbar --- */
--f-navbar-background: #b3b8ba;
--f-navbar-background: #e0e6eb;
--f-navbar-link: #0066cc;
--f-navbar-link-hover: #1a6655;
--f-navbar-link-hover: #ef233c;
/*--- Footer --- */
--f-footer-background: #ccd6db;
--f-footer-background: #e0e6eb;
--f-footer-link-text: #0066cc;
/* --- Linki --- */
--f-link-text: #0066cc;
--f-link-text-hover: #1a6655;
--f-link-text-hover: #ef233c;
--btn-background: #0066cc;
--btn-text: #ccd6db;
--btn-text: #e4ebee;
--btn-background-hover: #ccd6db;
--btn-text-hover: #0066cc;
@@ -60,6 +63,7 @@
/* Obramowanie footer w kanałów premium */
--f-premium-border: #adb5bd;
--f-premium-bg: #f7f5f5;
}
@@ -120,5 +124,6 @@ html.dark {
--f-addons-text: #fffae6;
--f-addons-background: #1a6655;
--f-premium-border: #3d3f40;
--f-premium-border: #6c757d;
--f-premium-bg: #343a40;
}