@tidyfactor/design 1.5.0 → 1.6.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 +9 -0
- package/README.ar.md +1 -1
- package/README.md +1 -1
- package/bin/add-skill.js +65 -31
- package/brand.json +3 -3
- package/manifest.json +59 -0
- package/package.json +2 -1
- package/references/memory/01-design-schools.md +49 -57
- package/references/memory/04-motion-principles.md +21 -36
- package/references/memory/05-component-anatomy.md +29 -32
- package/references/memory/08-arabic-bilingual.md +35 -47
- package/references/memory/19-heritage-lanes-atmosphere.md +92 -0
- package/references/schemas/audit_design.output.schema.json +47 -0
- package/scripts/audit_design.py +203 -0
package/.tidyfactor
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"ecosystem": "tidyfactor",
|
|
3
3
|
"track": "design",
|
|
4
4
|
"name": "tidyfactor-design",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.6.0",
|
|
6
6
|
"npmPackage": "@alwkala/tidyfactor-design",
|
|
7
7
|
"github": "https://github.com/TidyFactor/Design",
|
|
8
|
-
"skillFile": "../tidyfactor-design-v1.
|
|
8
|
+
"skillFile": "../tidyfactor-design-v1.6.0.skill",
|
|
9
9
|
"category": "design-system",
|
|
10
10
|
"type": "interactive-prototyping",
|
|
11
11
|
"outputs": [
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the **[@tidyfactor/design](https://www.npmjs.com/package/@tidyfactor/design)** package will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.6.0] - 2026-09-01
|
|
6
|
+
|
|
7
|
+
### Added - Runtime Tooling Manifest & Egyptian Heritage Design Lanes
|
|
8
|
+
- **Rule 10 Runtime Tooling Contract (`manifest.json`)**: Declared portable `audit_design`, `extract_palette`, and `optimize_media` executable tooling with schema validation.
|
|
9
|
+
- **Automated Design Quality Auditor (`scripts/audit_design.py`)**: Standalone and MCP-compatible CLI engine scanning HTML/CSS for unicode emojis, inline styles, untokenized colors, and the 16 AI anti-patterns with UTF-8 support.
|
|
10
|
+
- **Output JSON Schema (`references/schemas/audit_design.output.schema.json`)**: Standard schema defining structured quality audit reports and pre-emit critique stamps.
|
|
11
|
+
- **Egyptian & Regional Heritage Lanes (`references/memory/19-heritage-lanes-atmosphere.md`)**: Operational design grammar covering Nilotic Wadj & Lotus, Egyptian Nubian Folk & Earth Vaults, Sinai Bedouin Craft, Al-Muqarnas Geometry, and Kufic Modernity.
|
|
12
|
+
- **Bilingual & Responsive Enhancements**: Refined `01-design-schools.md`, `04-motion-principles.md`, `05-component-anatomy.md`, and `08-arabic-bilingual.md`.
|
|
13
|
+
|
|
5
14
|
## [1.5.0] - 2026-08-29
|
|
6
15
|
|
|
7
16
|
### Added - Global Multi-Tier & Multi-Language Documentation Architecture
|
package/README.ar.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<img src="assets/hero-banner.png" alt="TidyFactor Design Hero Banner" width="100%">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
# 🎨 TidyFactor Design `v1.
|
|
7
|
+
# 🎨 TidyFactor Design `v1.6.0`
|
|
8
8
|
### محرك دورة حياة تصميم الواجهات ومحرك النماذج التفاعلية المناهض للتكرار
|
|
9
9
|
|
|
10
10
|
**البداية الرسمية لبناء نظام التصميم والدورة الكاملة لتصميم الواجهات التفاعلية ضمن منظومة TidyFactor Ecosystem.**
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<img src="assets/hero-banner.png" alt="TidyFactor Design Hero Banner" width="100%">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
# 🎨 TidyFactor Design `v1.
|
|
7
|
+
# 🎨 TidyFactor Design `v1.6.0`
|
|
8
8
|
### Code-Native UI Design Lifecycle Engine & Anti-Slop Design System Suite
|
|
9
9
|
|
|
10
10
|
**The official UI design & interactive prototyping foundation for the TidyFactor Ecosystem.**
|
package/bin/add-skill.js
CHANGED
|
@@ -1,31 +1,65 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* bin/add-skill.js —
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/add-skill.js — Multi-Agent Skill Installer Wrapper for tidyfactor-design
|
|
4
|
+
* Supports Trae, Cursor, Windsurf, Antigravity, GitHub Copilot, RooCode, OpenCode, KiloCode, Warp, and Universal.
|
|
5
|
+
*
|
|
6
|
+
* @license Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
const targetDir = process.cwd();
|
|
14
|
+
const skillSource = path.resolve(__dirname, '..');
|
|
15
|
+
const skillName = 'tidyfactor-design';
|
|
16
|
+
|
|
17
|
+
const AGENT_MAP = [
|
|
18
|
+
{ name: 'Trae AI IDE', dir: path.join(targetDir, '.trae', 'skills', skillName), test: path.join(targetDir, '.trae') },
|
|
19
|
+
{ name: 'Cursor IDE', dir: path.join(targetDir, '.cursor', 'skills', skillName), test: path.join(targetDir, '.cursor') },
|
|
20
|
+
{ name: 'Windsurf Cascade', dir: path.join(targetDir, '.windsurf', 'skills', skillName), test: path.join(targetDir, '.windsurf') },
|
|
21
|
+
{ name: 'GitHub Copilot', dir: path.join(targetDir, '.github', 'prompts', skillName), test: path.join(targetDir, '.github') },
|
|
22
|
+
{ name: 'RooCode', dir: path.join(targetDir, '.roo', 'skills', skillName), test: path.join(targetDir, '.roo') },
|
|
23
|
+
{ name: 'OpenCode / Zen', dir: path.join(targetDir, '.opencode', 'skills', skillName), test: path.join(targetDir, '.opencode') },
|
|
24
|
+
{ name: 'KiloCode', dir: path.join(targetDir, '.kilocode', 'skills', skillName), test: path.join(targetDir, '.kilocode') },
|
|
25
|
+
{ name: 'Warp Terminal', dir: path.join(targetDir, '.warp', 'skills', skillName), test: path.join(targetDir, '.warp') },
|
|
26
|
+
{ name: 'Kiro Spec IDE', dir: path.join(targetDir, '.kiro', 'skills', skillName), test: path.join(targetDir, '.kiro') },
|
|
27
|
+
{ name: 'Claude Code', dir: path.join(targetDir, '.claude', 'skills', skillName), test: path.join(targetDir, '.claude') },
|
|
28
|
+
{ name: 'Zed AI Agent', dir: path.join(targetDir, '.zed', 'skills', skillName), test: path.join(targetDir, '.zed') },
|
|
29
|
+
{ name: 'Google Antigravity/Gemini', dir: path.join(targetDir, '.agents', 'skills', skillName), test: path.join(targetDir, '.agents') },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function copyRecursive(src, dest) {
|
|
33
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
34
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
const srcPath = path.join(src, entry.name);
|
|
37
|
+
const destPath = path.join(dest, entry.name);
|
|
38
|
+
if (['.git', 'node_modules', 'dist'].includes(entry.name)) continue;
|
|
39
|
+
if (entry.isDirectory()) {
|
|
40
|
+
copyRecursive(srcPath, destPath);
|
|
41
|
+
} else {
|
|
42
|
+
fs.copyFileSync(srcPath, destPath);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Determine installation targets
|
|
48
|
+
let mountedTargets = [];
|
|
49
|
+
|
|
50
|
+
for (const agent of AGENT_MAP) {
|
|
51
|
+
if (fs.existsSync(agent.test)) {
|
|
52
|
+
copyRecursive(skillSource, agent.dir);
|
|
53
|
+
mountedTargets.push(agent.name + ' (' + path.relative(targetDir, agent.dir) + ')');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Always ensure universal fallback in .agents/skills/
|
|
58
|
+
const defaultDir = path.join(targetDir, '.agents', 'skills', skillName);
|
|
59
|
+
copyRecursive(skillSource, defaultDir);
|
|
60
|
+
if (!mountedTargets.some(t => t.includes('.agents'))) {
|
|
61
|
+
mountedTargets.push('Universal Default (.agents/skills/' + skillName + ')');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('✨ Successfully injected ' + skillName + ' into:');
|
|
65
|
+
mountedTargets.forEach(t => console.log(' • ' + t));
|
package/brand.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "TidyFactor Design",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"schemaVersion": "brand-core-v2",
|
|
5
5
|
"meta": {
|
|
6
6
|
"product": "TidyFactor Design System",
|
|
7
7
|
"tagline": "Code-Native Interactive Prototyping Engine",
|
|
8
8
|
"description": "A zero-bundler, framework-free design system and UI prototyping engine with pluggable CSS foundations and full Arabic/RTL support.",
|
|
9
|
-
"version": "1.
|
|
10
|
-
"lastUpdated": "2026-
|
|
9
|
+
"version": "1.6.0",
|
|
10
|
+
"lastUpdated": "2026-09-01"
|
|
11
11
|
},
|
|
12
12
|
"identity": {
|
|
13
13
|
"logo": {
|
package/manifest.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_schema_version": "1.0.0",
|
|
3
|
+
"skill_id": "tidyfactor-design",
|
|
4
|
+
"skill_root_anchor": "self",
|
|
5
|
+
"tools": [
|
|
6
|
+
{
|
|
7
|
+
"name": "audit_design",
|
|
8
|
+
"description": "Scans index.html and design-system/*.css for emoji, inline styles, off-token colors, and the 16 AI anti-patterns.",
|
|
9
|
+
"entrypoint": { "runtime": "python3", "script": "scripts/audit_design.py" },
|
|
10
|
+
"invocation": {
|
|
11
|
+
"cli_pattern": "{runtime} \"{skill_root}/{script}\" --target \"{target_path}\" {flags}",
|
|
12
|
+
"args": [
|
|
13
|
+
{ "name": "target_path", "type": "path", "required": true, "description": "Absolute path to the client project being audited" },
|
|
14
|
+
{ "name": "fix", "type": "boolean", "required": false, "default": false, "flag": "--fix", "description": "Auto-fix violations where safe" }
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"output": {
|
|
18
|
+
"format": "json",
|
|
19
|
+
"channel": "stdout",
|
|
20
|
+
"schema_ref": "references/schemas/audit_design.output.schema.json"
|
|
21
|
+
},
|
|
22
|
+
"runtime_requirements": { "isolation_recommended": false, "dependencies": [] },
|
|
23
|
+
"invoked_by_commands": ["audit", "perf"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "extract_palette",
|
|
27
|
+
"description": "Computes WCAG AAA contrast ratios and generates tokens.css + brand.json from source colors.",
|
|
28
|
+
"entrypoint": { "runtime": "python3", "script": "scripts/extract_palette.py" },
|
|
29
|
+
"invocation": {
|
|
30
|
+
"cli_pattern": "{runtime} \"{skill_root}/{script}\" --target \"{target_path}\" {flags}",
|
|
31
|
+
"args": [
|
|
32
|
+
{ "name": "target_path", "type": "path", "required": true, "description": "Absolute path to the client project" },
|
|
33
|
+
{ "name": "source", "type": "string", "required": false, "flag": "--source", "description": "Hex seed color or image path to derive the palette from" }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"output": { "format": "json", "channel": "stdout" },
|
|
37
|
+
"runtime_requirements": { "isolation_recommended": false, "dependencies": [] },
|
|
38
|
+
"invoked_by_commands": ["tokens", "palette", "init"]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "optimize_media",
|
|
42
|
+
"description": "Compresses images, removes backgrounds, and generates WebP variants.",
|
|
43
|
+
"entrypoint": { "runtime": "python3", "script": "scripts/optimize_assets.py" },
|
|
44
|
+
"invocation": {
|
|
45
|
+
"cli_pattern": "{runtime} \"{skill_root}/{script}\" --target \"{target_path}\" {flags}",
|
|
46
|
+
"args": [
|
|
47
|
+
{ "name": "target_path", "type": "path", "required": true, "description": "Absolute path to the client project" },
|
|
48
|
+
{ "name": "remove_bg", "type": "boolean", "required": false, "default": false, "flag": "--remove-bg" }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"output": { "format": "json", "channel": "stdout" },
|
|
52
|
+
"runtime_requirements": {
|
|
53
|
+
"isolation_recommended": true,
|
|
54
|
+
"dependencies": ["Pillow", "rembg", "onnxruntime"]
|
|
55
|
+
},
|
|
56
|
+
"invoked_by_commands": ["assets"]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tidyfactor/design",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "TidyFactor Design track — code-native UI design lifecycle engine (Figma alternative) with Contextual Decision Layer (CDL)",
|
|
5
5
|
"main": "SKILL.md",
|
|
6
6
|
"bin": {
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"assets",
|
|
56
56
|
"bin",
|
|
57
57
|
"brand.json",
|
|
58
|
+
"manifest.json",
|
|
58
59
|
"memory",
|
|
59
60
|
"references",
|
|
60
61
|
"scripts",
|
|
@@ -1,13 +1,46 @@
|
|
|
1
|
-
# Design Schools & Movements —
|
|
1
|
+
# Design Schools & Movements — Unified Calibration Index
|
|
2
|
+
<!-- last-verified: 2026-09-01 -->
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
👉 **[`memory/
|
|
4
|
+
A complete working index of design aesthetics, universal movements, and regional heritage art-direction lanes across all TidyFactor design engines.
|
|
5
|
+
|
|
6
|
+
👉 **[`memory/16-design-movements-guide.md`](16-design-movements-guide.md)** (All 20 Universal Visual Movements)
|
|
7
|
+
👉 **[`memory/19-heritage-lanes-atmosphere.md`](19-heritage-lanes-atmosphere.md)** (5 Egyptian & Regional Heritage Lanes)
|
|
8
|
+
👉 **[`memory/17-storytelling-industries-ux.md`](17-storytelling-industries-ux.md)** (Storytelling Archetypes & Industry Registers)
|
|
6
9
|
👉 **[`memory/18-design-decision-engine.md`](18-design-decision-engine.md)** (9-Step Decision Tree & Anti-Design-Soup Constraint)
|
|
7
10
|
|
|
8
11
|
---
|
|
9
12
|
|
|
10
|
-
##
|
|
13
|
+
## 🏛️ 5 Regional & Egyptian Heritage Art-Direction Lanes
|
|
14
|
+
|
|
15
|
+
1. **Nilotic Wadj & Sacred Lotus (أصالة النيل ولوتس مصر العليا)**:
|
|
16
|
+
- *DNA*: Upper Egyptian heritage (Qena, Dendera, Thebes, Aswan), Sacred Lotus (`Sesen`), mineral green (`Wadj`), Nile terracotta (`Khem`), Royal Lapis (`Khesbed`), Sun Gold (`Nebu`).
|
|
17
|
+
- *Grammar*: Ascending registers, shallow stone deboss (`Low-Relief`), tactile papyrus & alabaster light texture (`Subtle Noise`).
|
|
18
|
+
- *Fits*: Community development, educational leadership, inclusive education, scholarly platforms, Upper Egyptian institutions.
|
|
19
|
+
|
|
20
|
+
2. **Egyptian Nubian Folk & Earth Vaults (النوبة المصرية والعمارة الطينية)**:
|
|
21
|
+
- *DNA*: Aswan & West Suhail mud-brick vaulted architecture (Hassan Fathy), geometric chevron & zigzag bands, vibrant folk warmth.
|
|
22
|
+
- *Grammar*: Stepped triangles, Nubian vaulted card tops, sunny ochre, turquoise cataracts, terracotta clay, white lime plaster.
|
|
23
|
+
- *Fits*: Cultural platforms, eco-tourism, community initiatives, youth & child development.
|
|
24
|
+
|
|
25
|
+
3. **Sinai Bedouin & Stitched Craft (البادية والتطريز السيناوي المصري)**:
|
|
26
|
+
- *DNA*: Authentic Sinai Bedouin cross-stitch embroidery (North & South Sinai), St. Catherine granite mountains, wild flora.
|
|
27
|
+
- *Grammar*: Pixelated cross-stitch geometric rails, embroidered dividers, madder red, wild rose magenta, deep Sinai night indigo, raw wool ivory.
|
|
28
|
+
- *Fits*: Heritage crafts, environmental foundations, field campaigns, national culture.
|
|
29
|
+
|
|
30
|
+
4. **Al-Muqarnas & Girih Geometry (التوريق والمقرنصات الهندسية)**:
|
|
31
|
+
- *DNA*: Historic Cairo (Fatimid & Mamluk), Andalusian & Maghrebi architecture, mashrabiya woodwork, mathematical astronomy.
|
|
32
|
+
- *Grammar*: 4/6/8-fold repeat geometry, architectural aperture masks, perforated shadows (`Perforated Shadowing`), cobalt lapis, turquoise glaze, antique ochre.
|
|
33
|
+
- *Fits*: Cultural foundations, academic conferences, endowment platforms, classical Islamic arts.
|
|
34
|
+
|
|
35
|
+
5. **Kufic Modernity & Luminescent Noor (العمارة الحروفية والنور المعاصر)**:
|
|
36
|
+
- *DNA*: Contemporary regional minimalism led by monumental Arabic typography and generous spatial purity.
|
|
37
|
+
- *Grammar*: Monumental Arabic wordmark/phrase hero, polished terrazzo, limestone, brushed bronze, single vibrant accent.
|
|
38
|
+
- *Fits*: Technology, sovereign wealth, venture studios, knowledge economy, international consulting.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🎨 20 Universal Visual Movements At-A-Glance Index
|
|
43
|
+
|
|
11
44
|
1. **Swiss Style**: Objective 12-column grid, sans-serif, high contrast.
|
|
12
45
|
2. **Flat Design**: 2D vector, zero shadows, vibrant color blocks.
|
|
13
46
|
3. **Material Design**: Paper/ink depth, elevation shadows, touch ripples.
|
|
@@ -29,57 +62,16 @@ For complete operational rules, visual tokens, typography pairings, color system
|
|
|
29
62
|
19. **Organic / Amorphous UI**: Fluid blob shapes (`border-radius: 60% 40% ...`), natural tones.
|
|
30
63
|
20. **Frutiger Aero & Y2K**: Glossy glass buttons, lens flares, vibrant sky blue/lime gradients.
|
|
31
64
|
|
|
32
|
-
|
|
33
|
-
Geometric shapes as structural/decorative elements, primary-plus-black
|
|
34
|
-
palettes, function-follows-form typography. Fits: education, design tools,
|
|
35
|
-
brands wanting to signal craft/foundational thinking. Avoid: costume-party
|
|
36
|
-
Bauhaus (circles+triangles as decoration with no structural logic).
|
|
37
|
-
|
|
38
|
-
## Brutalism (web)
|
|
39
|
-
Raw, unstyled-looking HTML elements used deliberately, harsh contrast,
|
|
40
|
-
visible grid lines, monospace accents, intentionally "undesigned". Fits:
|
|
41
|
-
developer tools, portfolios, brands wanting to signal authenticity/anti-
|
|
42
|
-
polish. Avoid: actual unstyled defaults mistaken for the aesthetic —
|
|
43
|
-
brutalism is a considered choice, not the absence of one.
|
|
44
|
-
|
|
45
|
-
## Editorial / Broadsheet
|
|
46
|
-
Hairline rules, dense multi-column text, serif display, byline/dateline
|
|
47
|
-
conventions borrowed from print journalism. Fits: publications, long-form
|
|
48
|
-
content, thought-leadership brands. Avoid: applying hairline-column density
|
|
49
|
-
to a page that has nothing to say at that density (see `frontend-design`'s
|
|
50
|
-
warning on this as an overused AI-design default).
|
|
51
|
-
|
|
52
|
-
## Minimalism
|
|
53
|
-
Extreme restraint, one accent color maximum, huge whitespace, type doing
|
|
54
|
-
almost all the work. Fits: luxury, premium single-product brands. Avoid:
|
|
55
|
-
minimalism as an excuse for lack of a real design decision — every
|
|
56
|
-
remaining element must be precisely placed.
|
|
57
|
-
|
|
58
|
-
## Glassmorphism
|
|
59
|
-
Frosted-glass translucent panels, soft shadows, layered depth over a
|
|
60
|
-
blurred background. Fits: modern consumer apps, dashboards wanting a soft/
|
|
61
|
-
approachable data-density feel. Avoid: layering glass panels on glass
|
|
62
|
-
panels — contrast and legibility break down fast.
|
|
63
|
-
|
|
64
|
-
## Material-influenced
|
|
65
|
-
Elevation via shadow, clear touch targets, motion as spatial logic (things
|
|
66
|
-
move the way physical objects would). Fits: cross-platform app UI,
|
|
67
|
-
utilitarian dashboards. Avoid: importing Material's specific component
|
|
68
|
-
shapes wholesale when the brand wants its own identity — take the
|
|
69
|
-
principles, not the skin.
|
|
65
|
+
---
|
|
70
66
|
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
## 🏆 Award-Winning Regional Reference Evidence
|
|
68
|
+
Treat award galleries as evidence of interaction and composition patterns:
|
|
69
|
+
- **Webook (Riyadh Season)**: Immersive event storytelling, WebGL and sound.
|
|
70
|
+
- **Masar Destination**: Saudi destination storytelling with responsive WebGL.
|
|
71
|
+
- **IoT Squared**: Sovereign technology positioning via animation & parallax without heritage cliches.
|
|
72
|
+
- **Kode**: New Cairo sports brand using bold color, animation, and WebGL.
|
|
73
|
+
- **Ali Ali**: Cairo creative portfolio driven by minimal typography and film.
|
|
74
|
+
- **Sivik Atelier**: Dubai studio portfolio using grid, parallax, and minimal typography.
|
|
75
|
+
- **Jazean Coffee**: Saudi heritage framed as interactive scroll storytelling.
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
Per `frontend-design`'s own calibration: warm-cream + terracotta-serif,
|
|
81
|
-
near-black + single acid-green/vermilion accent, and broadsheet-hairline-
|
|
82
|
-
columns applied regardless of subject are the three most common AI-design
|
|
83
|
-
tells right now. Choosing one deliberately because it fits the brief is
|
|
84
|
-
fine; landing on one by default is the failure mode `school` exists to
|
|
85
|
-
prevent.
|
|
77
|
+
*Rule: Establish a contemporary editorial system first, then use one culturally meaningful material, narrative, or spatial idea as the signature.*
|
|
@@ -1,44 +1,29 @@
|
|
|
1
1
|
# Motion Principles — Reference for `motion`
|
|
2
|
+
<!-- last-verified: 2026-09-01 -->
|
|
2
3
|
|
|
3
|
-
Adapted from
|
|
4
|
-
than character animation.
|
|
4
|
+
Adapted from classic animation principles, applied to UI motion rather than character animation.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
One focal change communicated at a time. A section revealing five cards
|
|
8
|
-
simultaneously reads as noise; the same five staggered by ~60-80ms each
|
|
9
|
-
read as a considered sequence.
|
|
6
|
+
---
|
|
10
7
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
## 1. Core Principles
|
|
9
|
+
- **Staging**: One focal change communicated at a time. Stagger card reveals by ~60-80ms.
|
|
10
|
+
- **Anticipation**: Small pre-state before a bigger change helps the eye track what's about to happen.
|
|
11
|
+
- **Ease-out for entrances, ease-in for exits**: Natural deceleration on entry, acceleration on exit.
|
|
12
|
+
- **Secondary motion**: Delayed subtle trail (e.g. shadow settling a beat later).
|
|
13
|
+
- **Restraint**: An orchestrated single moment lands harder than scattered effects everywhere.
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
Things entering the screen should decelerate into place (ease-out);
|
|
19
|
-
things leaving should accelerate away (ease-in). Using the same easing
|
|
20
|
-
for both reads as mechanical rather than natural.
|
|
15
|
+
---
|
|
21
16
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
## 2. Cursor Spotlight & Ambient Glow Engine (Interactive Depth)
|
|
18
|
+
- **Per-Card Cursor Spotlight**:
|
|
19
|
+
- Dynamically track `--mouse-x` and `--mouse-y` via `getBoundingClientRect()` on `.bento-card`, `.card`, and showcase tiles.
|
|
20
|
+
- Reveal a subtle `radial-gradient` (350-400px radius) in dark mode to illuminate surface borders and textures.
|
|
21
|
+
- **Viewport Ambient Spotlight**:
|
|
22
|
+
- Desktop-only (`min-width: 992px`), single fixed layer driven by `requestAnimationFrame` and `translate3d(x, y, 0)`.
|
|
23
|
+
- Illuminates the underlying papyrus, granite, or obsidian texture with zero layout thrashing.
|
|
24
|
+
- Must automatically disable under `prefers-reduced-motion: reduce`.
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
A primary element moving (e.g. a hero image entering) can carry a small,
|
|
30
|
-
delayed secondary motion (a shadow settling a beat later, an accent
|
|
31
|
-
element trailing slightly) — this is what separates "orchestrated" motion
|
|
32
|
-
from "everything moves in lockstep".
|
|
26
|
+
---
|
|
33
27
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
than scattered effects everywhere, and excess ambient animation is one of
|
|
37
|
-
the strongest tells that a design is AI-generated. `school`'s chosen
|
|
38
|
-
direction should determine how much motion is appropriate — Minimalism and
|
|
39
|
-
Swiss call for very little; Modern SaaS and Glassmorphism can carry more.
|
|
40
|
-
|
|
41
|
-
## Non-negotiable: `prefers-reduced-motion`
|
|
42
|
-
Every entrance/scroll/parallax effect in `motion.js` must check this media
|
|
43
|
-
query and fall back to instant or opacity-only transitions — applied
|
|
44
|
-
globally in the shared file, not per page.
|
|
28
|
+
## 3. Non-negotiable: `prefers-reduced-motion`
|
|
29
|
+
Every entrance, scroll, parallax, or ambient spotlight effect in `motion.js` must check `window.matchMedia('(prefers-reduced-motion: reduce)')` and fall back to instant or opacity-only transitions globally.
|
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
# Component Anatomy — Reference for `components`/`states`
|
|
2
|
+
<!-- last-verified: 2026-09-01 -->
|
|
2
3
|
|
|
3
|
-
## Atomic
|
|
4
|
-
- **Atoms
|
|
5
|
-
|
|
6
|
-
- **
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
If the only difference from an existing component is a token value (a
|
|
32
|
-
different accent color, a different size) — that's a modifier class or a
|
|
33
|
-
data attribute, not a new component. New components are for genuinely
|
|
34
|
-
different structure/behavior, not restyled instances.
|
|
4
|
+
## 1. Atomic Hierarchy (Atomic Design, Adapted)
|
|
5
|
+
- **Atoms**: Button, input, label, icon, badge, watermark stamp.
|
|
6
|
+
- **Molecules**: Form field, stat counter, nav item, floating verification badge.
|
|
7
|
+
- **Organisms**: Card, data table, navbar, 3D book/exhibition card, timeline, modal.
|
|
8
|
+
- **Templates**: Organisms arranged into page layout with zero per-page CSS.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 2. Advanced Specialized Organisms & Molecules
|
|
13
|
+
|
|
14
|
+
### A. Watermark Stamp & Media Protection (`.watermark-stamp`)
|
|
15
|
+
- Floating glassmorphic pill badge positioned over showcase media (e.g. `bottom: 12px; inset-inline-start: 12px;`).
|
|
16
|
+
- Visuals: `background: rgba(8, 7, 5, 0.85); backdrop-filter: blur(8px); border: 1px solid var(--border-gold);`.
|
|
17
|
+
- Protection: Non-draggable images (`-webkit-user-drag: none; user-select: none;`).
|
|
18
|
+
|
|
19
|
+
### B. 3D Exhibition & Editorial Book Showcase Card (`.book-card`)
|
|
20
|
+
- Layout: Asymmetric split (Cover viewport on one side, editorial hierarchy on the other).
|
|
21
|
+
- Visuals: Multi-layered spine shadow (`box-shadow: -8px 10px 24px rgba(0,0,0,0.25)`), dynamic hover lift (`translateY(-4px)`), and category badge.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 3. The Full 8-State Matrix (Apply per Interactive Component)
|
|
26
|
+
`default → hover → focus-visible → active/pressed → disabled`, plus: `loading` (spinner/skeleton), `empty` (illustrated container), `error`, and `success`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 4. Naming Discipline
|
|
31
|
+
One canonical name per real pattern. Use BEM modifiers (`.card--featured`, `.badge--accent`) or data attributes (`[data-theme]`, `[data-filter]`) rather than creating duplicate component structures.
|
|
@@ -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.
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
- Shallow stone relief deboss on cards (`Low-Relief`).
|
|
20
|
+
- Warm sunlit alabaster ambient glow.
|
|
21
|
+
* **Anti-Patterns**:
|
|
22
|
+
- Never use stock pyramids, pharaonic tourist masks, or fake pseudo-hieroglyphs.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🏛️ 2. Egyptian Nubian Folk & Earth Vaults (النوبة المصرية والعمارة الطينية)
|
|
27
|
+
* **Cultural DNA**: Aswan & West Suhail mud-brick vaulted architecture (Hassan Fathy tradition), geometric chevron/zigzag murals, and radiant folk warmth.
|
|
28
|
+
* **Palette**:
|
|
29
|
+
- `sun-ochre`: `#F59E0B`
|
|
30
|
+
- `aswan-turquoise`: `#0284C7`
|
|
31
|
+
- `nubian-clay`: `#C2410C`
|
|
32
|
+
- `nile-green`: `#10B981`
|
|
33
|
+
- `lime-plaster`: `#FFFFFF`
|
|
34
|
+
- `cataract-indigo`: `#0C2340`
|
|
35
|
+
* **Atmosphere & Texture**:
|
|
36
|
+
- Stepped triangles and chevron band dividers.
|
|
37
|
+
- Soft vaulted card headers (`border-top-left-radius: 28px; border-top-right-radius: 28px;`).
|
|
38
|
+
- Crisp lime plaster backgrounds with restrained vibrant accents.
|
|
39
|
+
* **Anti-Patterns**:
|
|
40
|
+
- Never scatter colors uncontrollably; maintain crisp white/lime grounds with disciplined geometric borders.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🏛️ 3. Sinai Bedouin & Stitched Craft (البادية والتطريز السيناوي المصري)
|
|
45
|
+
* **Cultural DNA**: Authentic Sinai Bedouin cross-stitch embroidery (North & South Sinai), St. Catherine granite peaks, desert flora, and hospitality.
|
|
46
|
+
* **Palette**:
|
|
47
|
+
- `sinai-madder`: `#B91C1C`
|
|
48
|
+
- `wild-rose`: `#BE185D`
|
|
49
|
+
- `sinai-night`: `#0F172A`
|
|
50
|
+
- `wadi-ochre`: `#D97706`
|
|
51
|
+
- `raw-wool`: `#F8F6F0`
|
|
52
|
+
- `antique-silver`: `#94A3B8`
|
|
53
|
+
* **Atmosphere & Texture**:
|
|
54
|
+
- Pixelated cross-stitch geometric borders (2px step grids).
|
|
55
|
+
- Embroidered rail lines under section headings.
|
|
56
|
+
- Granite stone texture and deep twilight indigo contrast.
|
|
57
|
+
* **Anti-Patterns**:
|
|
58
|
+
- Never place busy embroidery behind long body text; preserve high-contrast reading.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🏛️ 4. Al-Muqarnas & Girih Geometry (التوريق والمقرنصات الهندسية)
|
|
63
|
+
* **Cultural DNA**: Historic Cairo (Fatimid & Mamluk architecture), Andalusian zellij, mashrabiya woodwork, and Islamic mathematical astronomy.
|
|
64
|
+
* **Palette**:
|
|
65
|
+
- `cobalt-lapis`: `#0E3A60`
|
|
66
|
+
- `turquoise-glaze`: `#0284A5`
|
|
67
|
+
- `antique-gold`: `#C4922A`
|
|
68
|
+
- `stucco-white`: `#F9F8F5`
|
|
69
|
+
- `mashrabiya-walnut`: `#20150E`
|
|
70
|
+
* **Atmosphere & Texture**:
|
|
71
|
+
- Strict 4/6/8-fold geometric repeat networks.
|
|
72
|
+
- Aperture card masks and perforated shadow layers (`box-shadow` depth mimicking light through mashrabiya).
|
|
73
|
+
* **Anti-Patterns**:
|
|
74
|
+
- Never place sacred or Quranic text on interactive, clickable, or croppable UI.
|
|
75
|
+
- Never force arbitrary horseshoe arches on every element.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 🏛️ 5. Kufic Modernity & Luminescent Noor (العمارة الحروفية والنور المعاصر)
|
|
80
|
+
* **Cultural DNA**: Sovereign Arabic minimalism, monumental contemporary Kufic letterforms, spacious architecture, and luminous daylight.
|
|
81
|
+
* **Palette**:
|
|
82
|
+
- `modern-charcoal`: `#111827`
|
|
83
|
+
- `pure-alabaster`: `#FFFFFF`
|
|
84
|
+
- `limestone-grey`: `#E5E7EB`
|
|
85
|
+
- `brushed-bronze`: `#9A7B56`
|
|
86
|
+
- `single-accent`: Brand-specific vibrant hue (Electric Cyan / Mineral Teal)
|
|
87
|
+
* **Atmosphere & Texture**:
|
|
88
|
+
- Monumental typographic heroes (45-70% viewport scale).
|
|
89
|
+
- Honed terrazzo, brushed bronze, and limestone materials.
|
|
90
|
+
* **Anti-Patterns**:
|
|
91
|
+
- Never resort to tacky "gold on black" luxury clichés.
|
|
92
|
+
- Never squeeze Arabic letter tracking negatively.
|
|
@@ -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
|
+
}
|
|
@@ -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()
|