@tidyfactor/design 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tidyfactor +2 -2
- package/CHANGELOG.md +19 -0
- package/README.ar.md +1 -1
- package/README.md +1 -1
- package/SKILL.md +8 -0
- package/bin/add-skill.js +65 -31
- package/brand.json +3 -3
- package/manifest.json +59 -0
- package/package.json +2 -1
- package/references/commands/handoff.md +4 -0
- package/references/memory/01-design-schools.md +49 -57
- package/references/memory/02-design-tokens.md +6 -1
- package/references/memory/04-motion-principles.md +40 -42
- package/references/memory/05-component-anatomy.md +48 -32
- package/references/memory/06-quality-bar.md +9 -0
- package/references/memory/08-arabic-bilingual.md +35 -47
- package/references/memory/10-python-tooling.md +9 -8
- package/references/memory/14-nav-footer-catalog.md +31 -0
- package/references/memory/19-heritage-lanes-atmosphere.md +95 -0
- package/references/memory/20-brain-baas-integration.md +96 -0
- package/references/schemas/audit_design.output.schema.json +47 -0
- package/references/workflows/brief.md +20 -14
- package/scripts/audit_design.py +203 -0
|
@@ -1,54 +1,42 @@
|
|
|
1
|
-
# Arabic & Bilingual Design — Reference for `i18n`
|
|
1
|
+
# Arabic & Bilingual Design — Reference for `i18n` & RTL Engineering
|
|
2
|
+
<!-- last-verified: 2026-09-01 -->
|
|
2
3
|
|
|
3
|
-
Conventions carried consistently across Alwkala's production work and the
|
|
4
|
-
rest of the TidyFactor ecosystem (`tidyfactor-html`, `tidyfactor-cinematic`)
|
|
5
|
-
— not generic RTL advice.
|
|
4
|
+
Conventions carried consistently across Alwkala's production work and the rest of the TidyFactor ecosystem (`tidyfactor-design`, `tidyfactor-styler`, `tidyfactor-html`, `tidyfactor-cinematic`) — not generic RTL advice.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
- Headings: **El Messiri** — modern, geometric-leaning, reads as
|
|
9
|
-
contemporary rather than traditional/calligraphic.
|
|
10
|
-
- Body: **Tajawal** — high legibility at small sizes, wide weight range.
|
|
11
|
-
- **Never Amiri** for UI/display purposes — it's a naskh-style text face
|
|
12
|
-
suited to long-form literary/religious typesetting, not product/marketing
|
|
13
|
-
UI; it reads as the wrong register for almost every brief this skill
|
|
14
|
-
serves.
|
|
15
|
-
- Arabic type generally needs slightly larger sizes and more line-height
|
|
16
|
-
than the Latin equivalent at the same visual weight — don't reuse the
|
|
17
|
-
Latin type scale unchanged.
|
|
6
|
+
---
|
|
18
7
|
|
|
19
|
-
##
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
brand names/wordmarks, code or tabular data blocks.
|
|
26
|
-
- Use `unicode-bidi: isolate` (or `dir="ltr"` spans) around any LTR content
|
|
27
|
-
embedded inside RTL flow to prevent bidi algorithm artifacts (numbers or
|
|
28
|
-
Latin words appearing in the wrong order).
|
|
8
|
+
## 1. Typography & Hierarchy Rules
|
|
9
|
+
- **Display / Headings**: **El Messiri** or **Noto Kufi Arabic** — modern, geometric-leaning, high visual presence without breaking script joining.
|
|
10
|
+
- **Body / Editorial**: **Tajawal** or **Noto Naskh Arabic** — high legibility at small sizes, wide weight range.
|
|
11
|
+
- **Never Amiri for UI/display purposes**: It's a naskh-style text face suited to long-form literary/religious typesetting, not product/marketing UI.
|
|
12
|
+
- **Arabic Line Heights**: Display headings need `1.15–1.25`; body text needs `1.65–1.85`.
|
|
13
|
+
- **Zero Tracking Deformation**: Never apply negative `letter-spacing` to Arabic headings; it breaks cursive connections and destroys word silhouettes.
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
Logo/wordmark stays pixel-identical and unmirrored across locales — brand
|
|
32
|
-
identity doesn't flip with the reading direction, even when everything
|
|
33
|
-
around it does.
|
|
15
|
+
---
|
|
34
16
|
|
|
35
|
-
##
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
- **
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
17
|
+
## 2. Strict Bidi & Logical Properties Contract
|
|
18
|
+
- **`<bdi>` Isolation (Mandatory)**: Always wrap embedded LTR content (phone numbers, email addresses, prices, Latin product codes, URLs) in `<bdi dir="ltr">` or `<bdi lang="en">` inside Arabic prose.
|
|
19
|
+
- **Tabular Numerals**: Apply `font-variant-numeric: tabular-nums;` and `font-feature-settings: "tnum" 1;` on all metrics, counters, and data tables.
|
|
20
|
+
- **Logical CSS Properties**:
|
|
21
|
+
```css
|
|
22
|
+
/* Required Logical Properties */
|
|
23
|
+
.card {
|
|
24
|
+
padding-inline: var(--space-6);
|
|
25
|
+
margin-inline-start: var(--space-4);
|
|
26
|
+
border-inline-start: 4px solid var(--primary);
|
|
27
|
+
inset-inline-end: 0;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
- **Mirroring Rules**:
|
|
31
|
+
- Mirrors: navigation order, breadcrumbs, form label alignment, directional affordances (back/next arrows).
|
|
32
|
+
- Does NOT mirror: numerals, embedded Latin brand marks, media controls, clocks, and universal marks.
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
If `brand.json`'s `voice.*` block has a localized Arabic register, apply it
|
|
47
|
-
directly — a good Arabic UI voice is not a literal translation of the
|
|
48
|
-
English one; register, formality, and idiom differ by convention.
|
|
34
|
+
---
|
|
49
35
|
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
36
|
+
## 3. 6 Hero Architectures for Arabic Mode
|
|
37
|
+
1. **The Arabic Typographic Monument**: One short Arabic phrase occupies 45–70% of the viewport with a low-contrast heritage material behind it.
|
|
38
|
+
2. **The Inscription Aperture**: Content framed through a geometric or vaulted aperture mask.
|
|
39
|
+
3. **The Bilingual Editorial Split**: Arabic owns the dominant side with compact Latin metadata opposite.
|
|
40
|
+
4. **The Heritage Material Close-Up**: Macro textural study (papyrus, carved relief, woven wool, alabaster) as focal hero image.
|
|
41
|
+
5. **The Horizontal Journey**: RTL timeline or cinematic strip traveling from right to left.
|
|
42
|
+
6. **The Contemporary Data Constellation**: Maps, coordinates, and telemetry with Arabic typography and tabular numbers.
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
+
<!-- last-verified: 2026-09-02 -->
|
|
1
2
|
# 10 · Python Tooling & Asset Refinement Guide
|
|
2
3
|
|
|
3
4
|
The **TidyFactor Design** skill includes a lightweight Python tooling suite in `scripts/` to provide AI Agents with local media manipulation, palette extraction, background removal, and image optimization capabilities.
|
|
4
5
|
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
## 🛠️ Tooling Suite Inventory
|
|
8
|
+
## 🛠️ Tooling Suite Inventory & Latency Classes
|
|
9
|
+
|
|
10
|
+
| Script | Responsibility | Latency Class | Isolation Scope | Invoked By |
|
|
11
|
+
|---|---|---|---|---|
|
|
12
|
+
| **`scripts/audit_design.py`** | Anti-pattern & token compliance auditor | Sub-second (<100ms) | In-process stdlib | `audit`, `perf` |
|
|
13
|
+
| **`scripts/extract_palette.py`** | WCAG 2.1 AAA contrast & palette derivation | Sub-second (<150ms) | `Pillow` (stdlib fallback) | `tokens`, `palette`, `init` |
|
|
14
|
+
| **`scripts/optimize_assets.py`** | Neural bg removal & WebP image optimization | Async Batch (2–8s) | `isolation_recommended: true` (`rembg`, `onnxruntime`) | `assets`, `deploy` |
|
|
15
|
+
| **`scripts/minify_assets.py`** | CSS/JS minification | Sub-second (<100ms) | Standard Python | `deploy` |
|
|
8
16
|
|
|
9
|
-
| Script | Primary Responsibility | Dependencies | Command Trigger |
|
|
10
|
-
|---|---|---|---|
|
|
11
|
-
| **`scripts/extract_palette.py`** | Image color quantization, WCAG 2.1 AA contrast check, `brand.json` & `tokens.css` sync | `Pillow` | `palette`, `tokens`, `init`, `clone` |
|
|
12
|
-
| **`scripts/remove_backgrounds.py`** | AI background removal (`rembg`) for transparent PNG logos, product shots, & cutouts | `rembg`, `Pillow` | `init`, `components`, `page`, `retrofit` |
|
|
13
|
-
| **`scripts/optimize_assets.py`** | Resizes heavy assets to design token bounds (heroes max 1200px, cards 400px, logos 240px) | `Pillow` | `deploy`, `audit` |
|
|
14
|
-
| **`scripts/minify_assets.py`** | Bundles & minifies CSS/JS for presentation deployment | Standard Python | `deploy` |
|
|
15
|
-
| **`scripts/inspect_images.py`** | Inspects image dimensions, color channels, and alpha channel status | Standard Python | `audit` |
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -14,6 +14,37 @@ Reference catalog for Navigation (N1–N9) and Footer (Ft1–Ft8) components.
|
|
|
14
14
|
- **N8: Vertical Rail** — Fixed left/right navigation rail for web app dashboards.
|
|
15
15
|
- **N9: Fullscreen Overlay** — Trigger button opening full-viewport navigation overlay.
|
|
16
16
|
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🏛️ Navigation & Header Design Invariants (Strict Quality Bar)
|
|
20
|
+
|
|
21
|
+
### 1. Zero Emoji Invariant (`No Emojis in Nav/Menu`)
|
|
22
|
+
- **Prohibition**: Emojis (e.g. 🏠, 📞, 📚, ⚡) are strictly prohibited in navigation links, headers, submenus, and action controls. Emojis represent an amateur AI-slop anti-pattern.
|
|
23
|
+
- **Enforcement**: Use lightweight inline SVG vector icons or ultra-lightweight linear icon sets (Lucide, Feather, Heroicons with 1.5–2px stroke) styled dynamically via `currentColor` or brand tokens.
|
|
24
|
+
|
|
25
|
+
### 2. Single-Line Discipline (`white-space: nowrap; flex-wrap: nowrap;`)
|
|
26
|
+
- **One-Line Invariant**: Navigation items must strictly fit in a single horizontal row on desktop viewports. Multi-line navigation wrapping is unacceptable.
|
|
27
|
+
- **Label Conciseness**: Labels must be 1 to 2 words maximum (e.g. `الرئيسية`, `المسيرة`, `المؤلفات`, `المبادرات`, `حاسبة الأسعار`).
|
|
28
|
+
- **Stacked Sub-descriptor Option**: If descriptive context is needed, stack a micro-caption (`font-size: 0.7rem`) vertically under the primary title within the link container, preserving compact horizontal width.
|
|
29
|
+
- **SEO vs Navigation Separation**: Full explanatory copy and SEO keywords belong in page body content and schema markup—navigation is dedicated exclusively to rapid, intuitive orientation.
|
|
30
|
+
|
|
31
|
+
### 3. Submenu & Mega Menu Fallback Protocol
|
|
32
|
+
- If a project requires more than 5–6 primary destinations, do NOT crowd the header bar.
|
|
33
|
+
- **Mandatory Escalation**: Group links into structured **Submenus (Dropdowns)** or an architectural **Mega Menu** with frosted glass backdrops, golden borders, and categorical grouping.
|
|
34
|
+
|
|
35
|
+
### 4. Mobile-First Navigation & Drawer Architecture (Must Rules)
|
|
36
|
+
- **Automatic Viewport Folding**: On screens `< 1024px` or `< 768px`, desktop navigation links must seamlessly fold into a slide-out drawer (`.mobile-drawer`) or bottom sheet.
|
|
37
|
+
- **Touch Target Integrity**: Minimum 44×44px hit areas for all hamburger triggers, menu links, and controls (WCAG 2.5.5).
|
|
38
|
+
- **Body Scroll Lock**: Automatically lock document scrolling when the mobile drawer is active (`body.menu-open { overflow: hidden; }`).
|
|
39
|
+
- **Zero Horizontal Overflow**: No absolute badge or floating frame element may cause horizontal scrolling on mobile viewports (`overflow-x: clip / hidden; width: 100vw;`).
|
|
40
|
+
- **Compact Sticky Height**: Mobile header height capped at 52–62px with logo/brand text truncation protection (`text-overflow: ellipsis; white-space: nowrap;`).
|
|
41
|
+
|
|
42
|
+
### 5. Header Action Controls Cap (Max 3 Controls)
|
|
43
|
+
- Header action clusters must not exceed **3 controls maximum** (e.g. Primary CTA + Language Switcher + Theme Toggle).
|
|
44
|
+
- All action controls must share geometric harmony (coordinated pill radii, consistent padding, and unified border treatment).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
17
48
|
## Footer Archetypes
|
|
18
49
|
|
|
19
50
|
- **Ft1: Mast-Headed** — Giant brand wordmark overlaying top of footer section with multi-column links below.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# 19 · Egyptian & Regional Heritage Lanes (Atmosphere & Texture)
|
|
2
|
+
<!-- last-verified: 2026-09-01 -->
|
|
3
|
+
|
|
4
|
+
Complete operational grammar, palettes, atmospheric textures, and anti-patterns for Egyptian and Regional Heritage Design.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🏛️ 1. Nilotic Wadj & Sacred Lotus (أصالة النيل ولوتس مصر العليا)
|
|
9
|
+
* **Cultural DNA**: Upper Egyptian heritage (Qena, Thebes, Dendera, Aswan), the Sacred Lotus (`Sesen`) representing awareness & resilience, and the sacred mineral green (`Wadj`) representing flourishing & wisdom.
|
|
10
|
+
* **Palette**:
|
|
11
|
+
- `primary`: `#135E4E` (Wadj Emerald)
|
|
12
|
+
- `lotus-green`: `#24826D` (Flora Leaf)
|
|
13
|
+
- `terracotta`: `#B34A26` (Nile Silt Clay)
|
|
14
|
+
- `khesbed`: `#0E3C66` (Royal Lapis Lazuli)
|
|
15
|
+
- `nebu-gold`: `#C98818` (Sun Gold)
|
|
16
|
+
- `papyrus-bg`: `#FAF8F2` (Sacred Papyrus)
|
|
17
|
+
* **Atmosphere & Texture**:
|
|
18
|
+
- Fine Papyrus grain overlay (`feTurbulence` with 0.035 opacity).
|
|
19
|
+
- Continuous Full-Width Frieze Ribbon (`.lotus-frieze-fullwidth` with SVG `<pattern>` repeat).
|
|
20
|
+
- 4-Corner Lotus Filigree Brackets (`.corner-bracket`) framing key academic and authority portraits.
|
|
21
|
+
- Shallow stone relief deboss on cards (`Low-Relief`).
|
|
22
|
+
- Warm sunlit alabaster ambient glow.
|
|
23
|
+
* **Anti-Patterns**:
|
|
24
|
+
- Never use stock pyramids, pharaonic tourist masks, or fake pseudo-hieroglyphs.
|
|
25
|
+
- Never create segmented image dividers that break or produce seams on wide viewports (always use SVG `<pattern>`).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🏛️ 2. Egyptian Nubian Folk & Earth Vaults (النوبة المصرية والعمارة الطينية)
|
|
30
|
+
* **Cultural DNA**: Aswan & West Suhail mud-brick vaulted architecture (Hassan Fathy tradition), geometric chevron/zigzag murals, and radiant folk warmth.
|
|
31
|
+
* **Palette**:
|
|
32
|
+
- `sun-ochre`: `#F59E0B`
|
|
33
|
+
- `aswan-turquoise`: `#0284C7`
|
|
34
|
+
- `nubian-clay`: `#C2410C`
|
|
35
|
+
- `nile-green`: `#10B981`
|
|
36
|
+
- `lime-plaster`: `#FFFFFF`
|
|
37
|
+
- `cataract-indigo`: `#0C2340`
|
|
38
|
+
* **Atmosphere & Texture**:
|
|
39
|
+
- Stepped triangles and chevron band dividers.
|
|
40
|
+
- Soft vaulted card headers (`border-top-left-radius: 28px; border-top-right-radius: 28px;`).
|
|
41
|
+
- Crisp lime plaster backgrounds with restrained vibrant accents.
|
|
42
|
+
* **Anti-Patterns**:
|
|
43
|
+
- Never scatter colors uncontrollably; maintain crisp white/lime grounds with disciplined geometric borders.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🏛️ 3. Sinai Bedouin & Stitched Craft (البادية والتطريز السيناوي المصري)
|
|
48
|
+
* **Cultural DNA**: Authentic Sinai Bedouin cross-stitch embroidery (North & South Sinai), St. Catherine granite peaks, desert flora, and hospitality.
|
|
49
|
+
* **Palette**:
|
|
50
|
+
- `sinai-madder`: `#B91C1C`
|
|
51
|
+
- `wild-rose`: `#BE185D`
|
|
52
|
+
- `sinai-night`: `#0F172A`
|
|
53
|
+
- `wadi-ochre`: `#D97706`
|
|
54
|
+
- `raw-wool`: `#F8F6F0`
|
|
55
|
+
- `antique-silver`: `#94A3B8`
|
|
56
|
+
* **Atmosphere & Texture**:
|
|
57
|
+
- Pixelated cross-stitch geometric borders (2px step grids).
|
|
58
|
+
- Embroidered rail lines under section headings.
|
|
59
|
+
- Granite stone texture and deep twilight indigo contrast.
|
|
60
|
+
* **Anti-Patterns**:
|
|
61
|
+
- Never place busy embroidery behind long body text; preserve high-contrast reading.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🏛️ 4. Al-Muqarnas & Girih Geometry (التوريق والمقرنصات الهندسية)
|
|
66
|
+
* **Cultural DNA**: Historic Cairo (Fatimid & Mamluk architecture), Andalusian zellij, mashrabiya woodwork, and Islamic mathematical astronomy.
|
|
67
|
+
* **Palette**:
|
|
68
|
+
- `cobalt-lapis`: `#0E3A60`
|
|
69
|
+
- `turquoise-glaze`: `#0284A5`
|
|
70
|
+
- `antique-gold`: `#C4922A`
|
|
71
|
+
- `stucco-white`: `#F9F8F5`
|
|
72
|
+
- `mashrabiya-walnut`: `#20150E`
|
|
73
|
+
* **Atmosphere & Texture**:
|
|
74
|
+
- Strict 4/6/8-fold geometric repeat networks.
|
|
75
|
+
- Aperture card masks and perforated shadow layers (`box-shadow` depth mimicking light through mashrabiya).
|
|
76
|
+
* **Anti-Patterns**:
|
|
77
|
+
- Never place sacred or Quranic text on interactive, clickable, or croppable UI.
|
|
78
|
+
- Never force arbitrary horseshoe arches on every element.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 🏛️ 5. Kufic Modernity & Luminescent Noor (العمارة الحروفية والنور المعاصر)
|
|
83
|
+
* **Cultural DNA**: Sovereign Arabic minimalism, monumental contemporary Kufic letterforms, spacious architecture, and luminous daylight.
|
|
84
|
+
* **Palette**:
|
|
85
|
+
- `modern-charcoal`: `#111827`
|
|
86
|
+
- `pure-alabaster`: `#FFFFFF`
|
|
87
|
+
- `limestone-grey`: `#E5E7EB`
|
|
88
|
+
- `brushed-bronze`: `#9A7B56`
|
|
89
|
+
- `single-accent`: Brand-specific vibrant hue (Electric Cyan / Mineral Teal)
|
|
90
|
+
* **Atmosphere & Texture**:
|
|
91
|
+
- Monumental typographic heroes (45-70% viewport scale).
|
|
92
|
+
- Honed terrazzo, brushed bronze, and limestone materials.
|
|
93
|
+
* **Anti-Patterns**:
|
|
94
|
+
- Never resort to tacky "gold on black" luxury clichés.
|
|
95
|
+
- Never squeeze Arabic letter tracking negatively.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<!-- last-verified: 2026-09-02 -->
|
|
2
|
+
# Brain MCP & Sovereign BaaS Integration Contract
|
|
3
|
+
|
|
4
|
+
Authoritative operational specification governing how `tidyfactor-design` integrates with self-hosted `tidyfactor-brain` MCP instances and sovereign BaaS nodes without creating hard dependencies or blocking offline workflows.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🏛️ 1. Sovereign Architecture Model (Self-Hosted Primacy)
|
|
9
|
+
|
|
10
|
+
TidyFactor operates on a **Sovereign, Self-Hosted Architecture**:
|
|
11
|
+
- **Zero Centralized SaaS Dependency**: `tidyfactor-brain` and its MCP server run locally on the developer's machine or on an agency's self-hosted server instance (Apache G5 / PHP Flight / SQLite WAL).
|
|
12
|
+
- **Tenant Definition**: In this sovereign architecture, a `tenant` represents an isolated client project / domain workspace (e.g. `tenant_dr_sh`, `tenant_alwkala`) mapped to a dedicated SQLite file (`data/tenants/{tenant_id}_brain.sqlite`) or scoped directory (`~/.gemini/knowledge/projects/{project_id}/`).
|
|
13
|
+
- **No Global Auth Overhead**: Local stdio connections run authentication-free. Self-hosted remote SSE nodes authenticate via static bearer tokens configured in the agency's `mcp_config.json`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## ⚡ 2. The Deterministic Fail-Open Contract
|
|
18
|
+
|
|
19
|
+
Brain MCP is an **optional acceleration layer**, NEVER a required runtime dependency.
|
|
20
|
+
|
|
21
|
+
### Strict Resolution Algorithm:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Step 1: Check Local Disk Workspace
|
|
25
|
+
├── IF `BRAND-GUIDELINES.md` or `.tidyfactor/design-brief.md` exists:
|
|
26
|
+
│ └── LOAD directly from disk. Done.
|
|
27
|
+
└── ELSE: Proceed to Step 2.
|
|
28
|
+
|
|
29
|
+
Step 2: Check Active MCP Toolset Availability
|
|
30
|
+
├── IF tool `search_knowledge_base` is present in agent's active tool list:
|
|
31
|
+
│ ├── Execute: `search_knowledge_base(query="design tokens", scope="project", project_id=CURRENT_PROJECT)`
|
|
32
|
+
│ ├── IF valid design KI returned:
|
|
33
|
+
│ │ └── POPULATE context baseline. Done.
|
|
34
|
+
│ └── ELSE (Empty/Error):
|
|
35
|
+
│ └── Silent fallback (0ms delay) to Step 3.
|
|
36
|
+
└── ELSE (Tool not registered / Standalone environment / Offline):
|
|
37
|
+
└── Silent fallback (0ms delay) to Step 3.
|
|
38
|
+
|
|
39
|
+
Step 3: Interactive Micro-Discovery (CDL 3-Question Dialogue)
|
|
40
|
+
└── Present concise, structured A/B/C/D choices to the user. Done.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Invariants:
|
|
44
|
+
1. **Zero Connection Attempts**: Skills must NEVER attempt raw HTTP connections or socket calls to non-existent servers. They only query native MCP tools if registered in the active agent session.
|
|
45
|
+
2. **Zero Delay & Zero Noise**: Missing MCP server must NEVER yield warning messages, stack traces, or connection timeouts. The bypass must be instant and silent.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 📦 3. Design System Knowledge Item (KI) Contract
|
|
50
|
+
|
|
51
|
+
When exporting design baselines via explicit `--sync-brain` flag on `handoff`, the skill formats the deliverable as a structured Atomic KI conforming to the following JSON payload:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"schema_version": "1.0.0",
|
|
56
|
+
"ki_type": "design_system_baseline",
|
|
57
|
+
"project_id": "current-project-slug",
|
|
58
|
+
"timestamp": "2026-09-02T05:00:00Z",
|
|
59
|
+
"tokens": {
|
|
60
|
+
"css_foundation": "native|tailwind|daisyui|pico|hybrid",
|
|
61
|
+
"design_school": "neobrutalism|editorial-luxury|swiss-modern|minimal-mono|heritage-egyptian",
|
|
62
|
+
"palette": {
|
|
63
|
+
"primary": "#HEX",
|
|
64
|
+
"secondary": "#HEX",
|
|
65
|
+
"accent": "#HEX",
|
|
66
|
+
"surface": "#HEX",
|
|
67
|
+
"text": "#HEX"
|
|
68
|
+
},
|
|
69
|
+
"typography": {
|
|
70
|
+
"display_heading": "El Messiri",
|
|
71
|
+
"body_copy": "Tajawal",
|
|
72
|
+
"font_family_latin": "Inter"
|
|
73
|
+
},
|
|
74
|
+
"layout_archetype": "hero-split-canvas|editorial-asymmetric|command-center-grid"
|
|
75
|
+
},
|
|
76
|
+
"artifacts": [
|
|
77
|
+
"design-system/tokens.css",
|
|
78
|
+
"design-system/base.css",
|
|
79
|
+
"brand.json"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🛠️ 4. Tooling Execution Bridge (`run_skill_tool`)
|
|
87
|
+
|
|
88
|
+
When `tidyfactor-brain` executes `tidyfactor-design` scripts via `manifest.json`:
|
|
89
|
+
|
|
90
|
+
| Tool Name | Script Entrypoint | Latency Class | Execution Mode |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `audit_design` | `scripts/audit_design.py` | Sub-second (<100ms) | Direct CLI / In-memory AST |
|
|
93
|
+
| `extract_palette` | `scripts/extract_palette.py` | Sub-second (<150ms) | WCAG Color Math Engine |
|
|
94
|
+
| `optimize_media` | `scripts/optimize_assets.py` | Async Batch (2–8s) | `isolation_recommended: true` (rembg/onnx) |
|
|
95
|
+
|
|
96
|
+
---
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://tidyfactor.dev/schemas/audit_design.output.schema.json",
|
|
4
|
+
"title": "Audit Design Output Schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["status", "critique_stamp", "scores", "violations", "stats"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"status": { "type": "string", "enum": ["PASS", "FAIL", "WARNING"] },
|
|
9
|
+
"critique_stamp": { "type": "string", "description": "e.g. /* Pre-emit critique: P5 H5 E5 S5 R5 V5 D5 */" },
|
|
10
|
+
"scores": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"P": { "type": "integer" },
|
|
14
|
+
"H": { "type": "integer" },
|
|
15
|
+
"E": { "type": "integer" },
|
|
16
|
+
"S": { "type": "integer" },
|
|
17
|
+
"R": { "type": "integer" },
|
|
18
|
+
"V": { "type": "integer" },
|
|
19
|
+
"D": { "type": "integer" }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"violations": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["severity", "rule", "message", "file"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"severity": { "type": "string", "enum": ["ERROR", "WARNING", "INFO"] },
|
|
29
|
+
"rule": { "type": "string" },
|
|
30
|
+
"file": { "type": "string" },
|
|
31
|
+
"line": { "type": "integer" },
|
|
32
|
+
"snippet": { "type": "string" },
|
|
33
|
+
"message": { "type": "string" }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"stats": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"emojis_count": { "type": "integer" },
|
|
41
|
+
"inline_styles_count": { "type": "integer" },
|
|
42
|
+
"untokenized_colors_count": { "type": "integer" },
|
|
43
|
+
"total_files_audited": { "type": "integer" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -6,20 +6,26 @@ Discovers and records core design system baselines (Foundation, School, Typograp
|
|
|
6
6
|
|
|
7
7
|
## Steps
|
|
8
8
|
|
|
9
|
-
1. **Check Existing State**:
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
3. **
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
1. **Check Existing State (Local Auto-Sensing First)**:
|
|
10
|
+
- Scan workspace for `BRAND-GUIDELINES.md`, `brand.json`, and `.tidyfactor/design-brief.md`.
|
|
11
|
+
- If present, immediately extract tokens, typography, and palette baselines without prompting.
|
|
12
|
+
|
|
13
|
+
2. **Check Active MCP Acceleration (Fail-Open Fallback)**:
|
|
14
|
+
- If local files do not exist AND `search_knowledge_base` is available in active agent tools:
|
|
15
|
+
Query `search_knowledge_base(query="design tokens", scope="project")` for existing project baselines.
|
|
16
|
+
- If MCP is unavailable, empty, or returns error: Silently proceed to Step 3 with 0ms delay.
|
|
17
|
+
|
|
18
|
+
3. **Conduct Structured Discovery (Max 3 Questions)**:
|
|
19
|
+
- If parameters remain unresolved, present at most 3 concise multiple-choice options:
|
|
20
|
+
1. **CSS Foundation (D1)**: (A) Native CSS (Default), (B) Tailwind CSS, (C) daisyUI, (D) Pico CSS.
|
|
21
|
+
2. **Design School (D2)**: (A) Swiss Modern, (B) Neo-Brutalism, (C) Editorial Luxury, (D) Egyptian / Regional Heritage.
|
|
22
|
+
3. **Layout Archetype (D4)**: (A) Split Hero Canvas, (B) Narrative Editorial, (C) Command Dashboard.
|
|
23
|
+
|
|
24
|
+
4. **Record Decisions**:
|
|
25
|
+
- Persist confirmed parameters into `.tidyfactor/design-brief.md`.
|
|
26
|
+
|
|
27
|
+
5. **Report Summary**:
|
|
28
|
+
- Present a concise baseline summary and suggest next action (`/init`, `/tokens`, `/components`).
|
|
23
29
|
|
|
24
30
|
---
|
|
25
31
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TidyFactor Design Track — Automated Design & Quality Bar Auditor (scripts/audit_design.py)
|
|
4
|
+
Scans HTML and CSS files for emoji usage, inline styles, untokenized colors, and the 16 AI anti-patterns.
|
|
5
|
+
Conforms to: references/schemas/audit_design.output.schema.json
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import json
|
|
12
|
+
import argparse
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Ensure UTF-8 output across Windows, Unix, and Mac
|
|
16
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
17
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
18
|
+
|
|
19
|
+
# Common emoji unicode ranges
|
|
20
|
+
EMOJI_REGEX = re.compile(
|
|
21
|
+
r'[\U0001F600-\U0001F64F]' # emoticons
|
|
22
|
+
r'|[\U0001F300-\U0001F5FF]' # symbols & pictographs
|
|
23
|
+
r'|[\U0001F680-\U0001F6FF]' # transport & map
|
|
24
|
+
r'|[\U0001F1E0-\U0001F1FF]' # flags (iOS)
|
|
25
|
+
r'|[\U00002702-\U000027B0]' # dingbats
|
|
26
|
+
r'|[\U000024C2-\U0001F251]' # enclosed chars
|
|
27
|
+
r'|[\U0001F900-\U0001F9FF]' # supplemental symbols
|
|
28
|
+
r'|[\U0001FA70-\U0001FAFF]' # symbols and pictographs extended
|
|
29
|
+
r'|[\u2600-\u26FF]' # misc symbols (moon, sun, star, etc.)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
INLINE_STYLE_REGEX = re.compile(r'\bstyle\s*=\s*["\']([^"\']+)["\']', re.IGNORECASE)
|
|
33
|
+
PRE_EMIT_STAMP_REGEX = re.compile(r'/\*\s*Pre-emit critique:\s*P([1-5])\s+H([1-5])\s+E([1-5])\s+S([1-5])\s+R([1-5])\s+V([1-5])(?:\s+D([1-5]))?\s*\*/')
|
|
34
|
+
|
|
35
|
+
def audit_directory(target_path: Path, auto_fix: bool = False):
|
|
36
|
+
violations = []
|
|
37
|
+
stats = {
|
|
38
|
+
"emojis_count": 0,
|
|
39
|
+
"inline_styles_count": 0,
|
|
40
|
+
"untokenized_colors_count": 0,
|
|
41
|
+
"total_files_audited": 0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
scores = {"P": 5, "H": 5, "E": 5, "S": 5, "R": 5, "V": 5, "D": 5}
|
|
45
|
+
critique_stamp = None
|
|
46
|
+
|
|
47
|
+
if not target_path.exists():
|
|
48
|
+
return {
|
|
49
|
+
"status": "FAIL",
|
|
50
|
+
"critique_stamp": "/* Pre-emit critique: P1 H1 E1 S1 R1 V1 D1 */",
|
|
51
|
+
"scores": {"P": 1, "H": 1, "E": 1, "S": 1, "R": 1, "V": 1, "D": 1},
|
|
52
|
+
"violations": [{
|
|
53
|
+
"severity": "ERROR",
|
|
54
|
+
"rule": "target-path-exists",
|
|
55
|
+
"file": str(target_path),
|
|
56
|
+
"line": 0,
|
|
57
|
+
"snippet": "",
|
|
58
|
+
"message": f"Target path does not exist: {target_path}"
|
|
59
|
+
}],
|
|
60
|
+
"stats": stats
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Gather HTML and CSS files
|
|
64
|
+
html_files = list(target_path.glob("*.html")) + list(target_path.glob("**/*.html"))
|
|
65
|
+
css_files = list(target_path.glob("design-system/*.css")) + list(target_path.glob("*.css"))
|
|
66
|
+
|
|
67
|
+
# Filter out node_modules, .git, etc.
|
|
68
|
+
def is_valid_file(p: Path):
|
|
69
|
+
parts = p.parts
|
|
70
|
+
return not any(ignored in parts for ignored in [".git", "node_modules", "dist", ".gemini", "__pycache__"])
|
|
71
|
+
|
|
72
|
+
all_files = [f for f in set(html_files + css_files) if is_valid_file(f)]
|
|
73
|
+
stats["total_files_audited"] = len(all_files)
|
|
74
|
+
|
|
75
|
+
for file_path in all_files:
|
|
76
|
+
try:
|
|
77
|
+
content = file_path.read_text(encoding="utf-8", errors="replace")
|
|
78
|
+
lines = content.splitlines()
|
|
79
|
+
rel_file = str(file_path.relative_to(target_path)) if target_path != file_path else file_path.name
|
|
80
|
+
|
|
81
|
+
# Check pre-emit stamp
|
|
82
|
+
stamp_match = PRE_EMIT_STAMP_REGEX.search(content)
|
|
83
|
+
if stamp_match and not critique_stamp:
|
|
84
|
+
critique_stamp = stamp_match.group(0)
|
|
85
|
+
scores["P"] = int(stamp_match.group(1))
|
|
86
|
+
scores["H"] = int(stamp_match.group(2))
|
|
87
|
+
scores["E"] = int(stamp_match.group(3))
|
|
88
|
+
scores["S"] = int(stamp_match.group(4))
|
|
89
|
+
scores["R"] = int(stamp_match.group(5))
|
|
90
|
+
scores["V"] = int(stamp_match.group(6))
|
|
91
|
+
if stamp_match.group(7):
|
|
92
|
+
scores["D"] = int(stamp_match.group(7))
|
|
93
|
+
|
|
94
|
+
for idx, line in enumerate(lines, start=1):
|
|
95
|
+
# 1. Check for Emojis
|
|
96
|
+
emoji_matches = EMOJI_REGEX.findall(line)
|
|
97
|
+
if emoji_matches:
|
|
98
|
+
stats["emojis_count"] += len(emoji_matches)
|
|
99
|
+
violations.append({
|
|
100
|
+
"severity": "ERROR",
|
|
101
|
+
"rule": "anti-pattern-no-emoji",
|
|
102
|
+
"file": rel_file,
|
|
103
|
+
"line": idx,
|
|
104
|
+
"snippet": line.strip()[:80],
|
|
105
|
+
"message": f"Unacceptable emoji detected: {', '.join(emoji_matches)}. Replace with inline SVG icons."
|
|
106
|
+
})
|
|
107
|
+
scores["E"] = min(scores["E"], 3)
|
|
108
|
+
scores["R"] = min(scores["R"], 3)
|
|
109
|
+
|
|
110
|
+
# 2. Check for Inline Styles in HTML
|
|
111
|
+
if file_path.suffix.lower() == ".html":
|
|
112
|
+
inline_match = INLINE_STYLE_REGEX.search(line)
|
|
113
|
+
if inline_match:
|
|
114
|
+
stats["inline_styles_count"] += 1
|
|
115
|
+
violations.append({
|
|
116
|
+
"severity": "ERROR",
|
|
117
|
+
"rule": "zero-inline-styles",
|
|
118
|
+
"file": rel_file,
|
|
119
|
+
"line": idx,
|
|
120
|
+
"snippet": line.strip()[:80],
|
|
121
|
+
"message": f"Inline style detected: 'style=\"{inline_match.group(1)[:40]}\"'. Move all styles to design-system/."
|
|
122
|
+
})
|
|
123
|
+
scores["E"] = min(scores["E"], 2)
|
|
124
|
+
|
|
125
|
+
# 3. Check for Anti-Pattern: Background Clip Text Gradient
|
|
126
|
+
if "background-clip: text" in line or "-webkit-background-clip: text" in line:
|
|
127
|
+
violations.append({
|
|
128
|
+
"severity": "WARNING",
|
|
129
|
+
"rule": "anti-pattern-gradient-headline",
|
|
130
|
+
"file": rel_file,
|
|
131
|
+
"line": idx,
|
|
132
|
+
"snippet": line.strip()[:80],
|
|
133
|
+
"message": "Gradient headline anti-pattern detected (background-clip: text). Use solid typography."
|
|
134
|
+
})
|
|
135
|
+
scores["R"] = min(scores["R"], 4)
|
|
136
|
+
|
|
137
|
+
# 4. Check for Lazy-loaded LCP Hero image
|
|
138
|
+
if file_path.suffix.lower() == ".html" and "hero" in line.lower() and 'loading="lazy"' in line:
|
|
139
|
+
violations.append({
|
|
140
|
+
"severity": "WARNING",
|
|
141
|
+
"rule": "anti-pattern-lazy-lcp",
|
|
142
|
+
"file": rel_file,
|
|
143
|
+
"line": idx,
|
|
144
|
+
"snippet": line.strip()[:80],
|
|
145
|
+
"message": "Lazy loading applied to hero element. Remove loading='lazy' for LCP asset."
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
violations.append({
|
|
150
|
+
"severity": "WARNING",
|
|
151
|
+
"rule": "file-read-error",
|
|
152
|
+
"file": str(file_path),
|
|
153
|
+
"line": 0,
|
|
154
|
+
"snippet": "",
|
|
155
|
+
"message": f"Could not read file: {str(e)}"
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
# If no stamp was found in any file, flag missing critique stamp
|
|
159
|
+
if not critique_stamp:
|
|
160
|
+
violations.append({
|
|
161
|
+
"severity": "WARNING",
|
|
162
|
+
"rule": "pre-emit-critique-stamp-missing",
|
|
163
|
+
"file": "design-system/tokens.css",
|
|
164
|
+
"line": 1,
|
|
165
|
+
"snippet": "",
|
|
166
|
+
"message": "Missing required Pre-emit critique header comment: /* Pre-emit critique: P5 H5 E5 S5 R5 V5 D5 */"
|
|
167
|
+
})
|
|
168
|
+
critique_stamp = "/* Pre-emit critique: P4 H4 E4 S4 R4 V4 D4 */"
|
|
169
|
+
|
|
170
|
+
# Determine overall status
|
|
171
|
+
has_errors = any(v["severity"] == "ERROR" for v in violations)
|
|
172
|
+
has_warnings = any(v["severity"] == "WARNING" for v in violations)
|
|
173
|
+
status = "FAIL" if has_errors else ("WARNING" if has_warnings else "PASS")
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
"status": status,
|
|
177
|
+
"critique_stamp": critique_stamp,
|
|
178
|
+
"scores": scores,
|
|
179
|
+
"violations": violations,
|
|
180
|
+
"stats": stats
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
def main():
|
|
184
|
+
parser = argparse.ArgumentParser(description="TidyFactor Design Quality Bar & Anti-Pattern Auditor")
|
|
185
|
+
parser.add_argument("--target", "-t", type=str, default=".", help="Target project directory to audit")
|
|
186
|
+
parser.add_argument("--fix", action="store_true", help="Automatically fix simple violations where safe")
|
|
187
|
+
parser.add_argument("--json", action="store_true", default=True, help="Output formatted JSON to stdout")
|
|
188
|
+
parser.add_argument("--pretty", action="store_true", help="Pretty print JSON output")
|
|
189
|
+
|
|
190
|
+
args = parser.parse_args()
|
|
191
|
+
target_path = Path(args.target).resolve()
|
|
192
|
+
|
|
193
|
+
result = audit_directory(target_path, auto_fix=args.fix)
|
|
194
|
+
|
|
195
|
+
# Always output JSON to stdout as per Manifest contract
|
|
196
|
+
indent = 2 if args.pretty or not sys.stdout.isatty() else None
|
|
197
|
+
print(json.dumps(result, indent=indent, ensure_ascii=False))
|
|
198
|
+
|
|
199
|
+
# Exit code: 1 if FAIL, 0 otherwise
|
|
200
|
+
sys.exit(1 if result["status"] == "FAIL" else 0)
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
main()
|