@newlogic-digital/core 4.0.0-rc.8 → 4.0.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/bin/newlogic-core.js +16 -0
- package/index.js +1 -0
- package/package.json +27 -12
- package/skills/figma-implement-design/SKILL.md +246 -0
- package/skills/figma-implement-design/agents/openai.yaml +14 -0
- package/skills/figma-implement-design/assets/figma-small.svg +3 -0
- package/skills/figma-implement-design/assets/figma.png +0 -0
- package/skills/figma-implement-design/assets/icon.svg +28 -0
- package/skills/newlogic-ui/SKILL.md +33 -0
- package/skills/newlogic-ui/references/assets-guide.md +126 -0
- package/skills/newlogic-ui/references/project-guide.md +25 -0
- package/skills/newlogic-ui/references/scripting-guide.md +38 -0
- package/skills/newlogic-ui/references/styling-guide.md +75 -0
- package/skills/newlogic-ui/references/templating-guide.md +106 -0
- package/skills/newlogic-ui/references/theme-guide.md +23 -0
- package/src/linkAgentSkills.js +107 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { styleText } from 'node:util'
|
|
4
|
+
import { getPackageInfo } from 'vituum/utils/common.js'
|
|
5
|
+
|
|
6
|
+
const { name } = getPackageInfo(new URL('../package.json', import.meta.url).href)
|
|
7
|
+
const [command] = process.argv.slice(2)
|
|
8
|
+
|
|
9
|
+
if (command === 'postinstall') {
|
|
10
|
+
await import('../src/createEslintStylisticConfig.js')
|
|
11
|
+
await import('../src/linkAgentSkills.js')
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
console.error(`${styleText(['cyan', 'bold'], name)} ${styleText('red', `Unknown command "${command ?? ''}".`)}`)
|
|
15
|
+
process.exitCode = 1
|
|
16
|
+
}
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newlogic-digital/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.0.0
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"newlogic-core": "./bin/newlogic-core.js"
|
|
8
|
+
},
|
|
6
9
|
"author": "New Logic Studio s.r.o.",
|
|
7
10
|
"description": "Set of tools that can be used to create modern web applications",
|
|
8
11
|
"license": "MIT",
|
|
9
12
|
"scripts": {
|
|
10
|
-
"postinstall": "node ./src/createEslintStylisticConfig.js",
|
|
11
13
|
"tsc": "tsc",
|
|
12
14
|
"oxlint": "oxlint",
|
|
13
15
|
"oxlint-fix": "oxlint --fix",
|
|
@@ -16,30 +18,43 @@
|
|
|
16
18
|
},
|
|
17
19
|
"dependencies": {
|
|
18
20
|
"@minify-html/node": "^0.18.1",
|
|
19
|
-
"@newlogic-digital/vite-plugin-heroicons": "^1.1.
|
|
21
|
+
"@newlogic-digital/vite-plugin-heroicons": "^1.1.1",
|
|
20
22
|
"@stylistic/eslint-plugin": "^5.10",
|
|
21
23
|
"@stylistic/stylelint-config": "^4.0.0",
|
|
22
|
-
"@vituum/vite-plugin-css-inline": "^2.0.
|
|
23
|
-
"@vituum/vite-plugin-latte": "^2.0.
|
|
24
|
-
"@vituum/vite-plugin-send": "^2.0.
|
|
24
|
+
"@vituum/vite-plugin-css-inline": "^2.0.2",
|
|
25
|
+
"@vituum/vite-plugin-latte": "^2.0.2",
|
|
26
|
+
"@vituum/vite-plugin-send": "^2.0.1",
|
|
25
27
|
"browserslist": "^4.28.1",
|
|
26
28
|
"browserslist-to-esbuild": "^2.1.1",
|
|
27
29
|
"lightningcss": "^1.32.0",
|
|
30
|
+
"npm-check-updates": "^19.6.5",
|
|
28
31
|
"oxlint": "^1.56.0",
|
|
29
32
|
"stylelint-config-standard": "^40.0.0",
|
|
30
|
-
"vituum": "^2.0.
|
|
33
|
+
"vituum": "^2.0.1"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@tailwindcss/vite": "4.2.2",
|
|
37
|
+
"@vituum/vite-plugin-twig": "^2.0.1",
|
|
38
|
+
"vite": "^8.0.1"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"@vituum/vite-plugin-twig": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"@tailwindcss/vite": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
31
47
|
},
|
|
32
48
|
"devDependencies": {
|
|
33
|
-
"@tailwindcss/vite": "0.0.0-insiders.d24b112",
|
|
34
49
|
"@types/node": "^25.5",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"typescript": "^5",
|
|
38
|
-
"vite": "^8.0.0"
|
|
50
|
+
"rolldown": "^1.0.0-rc.10",
|
|
51
|
+
"typescript": "^5"
|
|
39
52
|
},
|
|
40
53
|
"files": [
|
|
54
|
+
"bin",
|
|
41
55
|
"icons",
|
|
42
56
|
"latte",
|
|
57
|
+
"skills",
|
|
43
58
|
"src",
|
|
44
59
|
"types",
|
|
45
60
|
"eslint-stylistic.json",
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-implement-design
|
|
3
|
+
description: Translate Figma nodes into production-ready code with 1:1 visual fidelity using the Figma MCP workflow (design context, screenshots, assets, and project-convention translation). Trigger when the user provides Figma URLs or node IDs, or asks to implement designs or components that must match Figma specs. Requires a working Figma MCP server connection.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Implement Design
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This skill provides a structured workflow for translating Figma designs into production-ready code with pixel-perfect accuracy. It ensures consistent integration with the Figma MCP server, proper use of design tokens, and 1:1 visual parity with designs.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- Figma MCP server must be connected and accessible
|
|
15
|
+
- User must provide a Figma URL in the format: `https://figma.com/design/:fileKey/:fileName?node-id=1-2`
|
|
16
|
+
- `:fileKey` is the file key
|
|
17
|
+
- `1-2` is the node ID (the specific component or frame to implement)
|
|
18
|
+
- **OR** when using `figma-desktop` MCP: User can select a node directly in the Figma desktop app (no URL required)
|
|
19
|
+
- Project should have an established design system or component library (preferred)
|
|
20
|
+
|
|
21
|
+
### Step 1: Get Node ID
|
|
22
|
+
|
|
23
|
+
#### Option A: Parse from Figma URL
|
|
24
|
+
|
|
25
|
+
When the user provides a Figma URL, extract the file key and node ID to pass as arguments to MCP tools.
|
|
26
|
+
|
|
27
|
+
**URL format:** `https://figma.com/design/:fileKey/:fileName?node-id=1-2`
|
|
28
|
+
|
|
29
|
+
**Extract:**
|
|
30
|
+
|
|
31
|
+
- **File key:** `:fileKey` (the segment after `/design/`)
|
|
32
|
+
- **Node ID:** `1-2` (the value of the `node-id` query parameter)
|
|
33
|
+
|
|
34
|
+
**Note:** When using the local desktop MCP (`figma-desktop`), `fileKey` is not passed as a parameter to tool calls. The server automatically uses the currently open file, so only `nodeId` is needed.
|
|
35
|
+
|
|
36
|
+
**Example:**
|
|
37
|
+
|
|
38
|
+
- URL: `https://figma.com/design/kL9xQn2VwM8pYrTb4ZcHjF/DesignSystem?node-id=42-15`
|
|
39
|
+
- File key: `kL9xQn2VwM8pYrTb4ZcHjF`
|
|
40
|
+
- Node ID: `42-15`
|
|
41
|
+
|
|
42
|
+
#### Option B: Use Current Selection from Figma Desktop App (figma-desktop MCP only)
|
|
43
|
+
|
|
44
|
+
When using the `figma-desktop` MCP and the user has NOT provided a URL, the tools automatically use the currently selected node from the open Figma file in the desktop app.
|
|
45
|
+
|
|
46
|
+
**Note:** Selection-based prompting only works with the `figma-desktop` MCP server. The remote server requires a link to a frame or layer to extract context. The user must have the Figma desktop app open with a node selected.
|
|
47
|
+
|
|
48
|
+
### Step 2: Fetch Design Context
|
|
49
|
+
|
|
50
|
+
Run `get_design_context` with the extracted file key and node ID.
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
get_design_context(fileKey=":fileKey", nodeId="1-2")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This provides the structured data including:
|
|
57
|
+
|
|
58
|
+
- Layout properties (Auto Layout, constraints, sizing)
|
|
59
|
+
- Typography specifications
|
|
60
|
+
- Color values and design tokens
|
|
61
|
+
- Component structure and variants
|
|
62
|
+
- Spacing and padding values
|
|
63
|
+
|
|
64
|
+
**If the response is too large or truncated:**
|
|
65
|
+
|
|
66
|
+
1. Run `get_metadata(fileKey=":fileKey", nodeId="1-2")` to get the high-level node map
|
|
67
|
+
2. Identify the specific child nodes needed from the metadata
|
|
68
|
+
3. Fetch individual child nodes with `get_design_context(fileKey=":fileKey", nodeId=":childNodeId")`
|
|
69
|
+
4. Run `get_variable_defs(fileKey=":fileKey", nodeId="1-2")` to get the variable definitions for the child nodes
|
|
70
|
+
|
|
71
|
+
### Step 3: Resolve Code Connect Mappings
|
|
72
|
+
|
|
73
|
+
Run `get_code_connect_map` on the same top-level node before implementation starts.
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
get_code_connect_map(fileKey=":fileKey", nodeId="1-2")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This step is mandatory for a correct design-system handoff.
|
|
80
|
+
|
|
81
|
+
**Interpretation rules:**
|
|
82
|
+
|
|
83
|
+
- If `nodeId="1-2"` is a page, frame, or large section, treat the returned map as the authoritative list of mapped descendant components that MUST be preserved in the final handoff.
|
|
84
|
+
- Do NOT assume the explicit `CodeConnectSnippet` wrappers shown in `get_design_context` are the full list. A page-level `get_code_connect_map` may return many more mapped descendants.
|
|
85
|
+
- `CodeConnectSnippet` wrapper elements are never part of final code. Use only the inner snippet or the actual mapped component in the codebase.
|
|
86
|
+
- Do NOT rename Code Connect classes, collapse modifier classes into new aliases, or replace mapped snippet APIs with custom helper classes just because the implementation is standalone or the classes do not exist yet. Implement the missing styles behind the mapped contract instead.
|
|
87
|
+
- If you must diverge from the mapped markup or class API for technical reasons, STOP and get explicit user approval first. Explain the exact incompatibility and the minimal required deviation.
|
|
88
|
+
|
|
89
|
+
### Step 3: Capture Visual Reference
|
|
90
|
+
|
|
91
|
+
Run `get_screenshot` with the same file key and node ID for a visual reference.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
get_screenshot(fileKey=":fileKey", nodeId="1-2")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This screenshot serves as the source of truth for visual validation. Keep it accessible throughout implementation.
|
|
98
|
+
|
|
99
|
+
### Step 4: Download Assets
|
|
100
|
+
|
|
101
|
+
Download any assets (images, icons, SVGs) returned by the Figma MCP server, BUT ONLY if the user specifically asks, otherwise skip this step.
|
|
102
|
+
|
|
103
|
+
**IMPORTANT:** Follow these asset rules:
|
|
104
|
+
|
|
105
|
+
- NEVER invent, redraw, approximate, reconstruct, or substitute assets that already exist in the Figma payload. If Figma provides an icon, SVG, image, or vector, use that original asset or exact markup. If you are about to replace an existing asset ALWAYS STOP AND ASK the user first.
|
|
106
|
+
- If the original Figma code already contains the asset markup, preserve or translate that exact asset instead of recreating it by hand.
|
|
107
|
+
- Assets are served through the Figma MCP server's built-in assets endpoint
|
|
108
|
+
- If the user or other skill specifies to use placeholder assets, or icon packs, follow the provided guidelines and do NOT use figma assets.
|
|
109
|
+
- If an asset URL or payload cannot be consumed in the target runtime, treat that as a blocker for exact fidelity. Report the failure clearly and ask the user before recreating, approximating, or substituting the asset.
|
|
110
|
+
|
|
111
|
+
### Step 5: Translate to Project Conventions
|
|
112
|
+
|
|
113
|
+
Translate the Figma output into this project's framework, styles, and conventions.
|
|
114
|
+
|
|
115
|
+
**Key principles:**
|
|
116
|
+
|
|
117
|
+
- Treat the Figma MCP output (typically React + Tailwind) as a representation of design and behavior, not as final code style
|
|
118
|
+
- Treat Code Connect mappings as authoritative handoff requirements, not optional hints
|
|
119
|
+
- Review that the Tailwind utility classes are using the latest Tailwind conventions and patterns (MUST be version 4 and above)
|
|
120
|
+
- Always use Tailwind CSS v4. If the project does not already have a Tailwind build pipeline, use the browser build from `https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4`. Do NOT use the legacy `https://cdn.tailwindcss.com` script.
|
|
121
|
+
- Reuse existing components (buttons, inputs, typography, icon wrappers) instead of duplicating functionality
|
|
122
|
+
- Search the codebase for mapped component sources before falling back to raw snippets
|
|
123
|
+
- Use the project's color system, typography scale, and spacing tokens consistently
|
|
124
|
+
- Respect existing routing, state management, and data-fetch patterns
|
|
125
|
+
- Preserve the original Figma layout and DOM structure as closely as possible. Do NOT redesign, simplify, restructure, or "improve" the layout unless responsiveness, technical constraints, or an explicit user request requires it.
|
|
126
|
+
- Responsiveness is the default exception: adapt layout only as much as needed for smaller breakpoints while keeping the desktop structure faithful to the original frame.
|
|
127
|
+
- If you are about to make a large structural change, invent missing structure, or replace original layout patterns with new ones, STOP and ASK the user first.
|
|
128
|
+
- If Code Connect provides raw HTML class contracts instead of framework components, preserve those contracts in the final output. Add CSS/Tailwind support for them if needed, but do not silently translate them into a different API.
|
|
129
|
+
|
|
130
|
+
### Step 6: Achieve 1:1 Visual Parity
|
|
131
|
+
|
|
132
|
+
Strive for pixel-perfect visual parity with the Figma design.
|
|
133
|
+
|
|
134
|
+
**Guidelines:**
|
|
135
|
+
|
|
136
|
+
- Prioritize Figma fidelity to match designs exactly
|
|
137
|
+
- Avoid hardcoded values - use design tokens from Figma where available
|
|
138
|
+
- When conflicts arise between design system tokens and Figma specs, prefer design system tokens but adjust spacing or sizes minimally to match visuals
|
|
139
|
+
- Follow WCAG requirements for accessibility
|
|
140
|
+
- Add component documentation as needed
|
|
141
|
+
|
|
142
|
+
### Step 7: Validate Against Figma
|
|
143
|
+
|
|
144
|
+
Before marking complete, validate the final UI against the Figma screenshot.
|
|
145
|
+
|
|
146
|
+
**Validation checklist:**
|
|
147
|
+
|
|
148
|
+
- [ ] Layout matches (spacing, alignment, sizing)
|
|
149
|
+
- [ ] Typography matches (font, size, weight, line height)
|
|
150
|
+
- [ ] Colors match exactly
|
|
151
|
+
- [ ] Interactive states work as designed (hover, active, disabled)
|
|
152
|
+
- [ ] Responsive behavior follows Figma constraints
|
|
153
|
+
- [ ] Assets render correctly
|
|
154
|
+
- [ ] Every descendant returned by `get_code_connect_map` on the top-level node is represented in the final code
|
|
155
|
+
- [ ] Accessibility standards met
|
|
156
|
+
|
|
157
|
+
### Step 8: Validate Against Browser
|
|
158
|
+
|
|
159
|
+
Use the /agent-browser skill if exists to validate the final UI in the browser.
|
|
160
|
+
|
|
161
|
+
- If the skill does not exist, skip this step.
|
|
162
|
+
- If the skill command fails, ask the user to manually validate the UI in the browser and skip this step.
|
|
163
|
+
|
|
164
|
+
## Implementation Rules
|
|
165
|
+
|
|
166
|
+
### Component Organization
|
|
167
|
+
|
|
168
|
+
- Place UI components in the project's designated design system directory
|
|
169
|
+
- Follow the project's component naming conventions
|
|
170
|
+
- Avoid inline styles unless truly necessary for dynamic values
|
|
171
|
+
|
|
172
|
+
### Design System Integration
|
|
173
|
+
|
|
174
|
+
- ALWAYS use components from the project's design system when possible
|
|
175
|
+
- ALWAYS preserve mapped Code Connect components in the final handoff when they exist
|
|
176
|
+
- When using mapped Code Connect snippets directly, preserve their public contract exactly: same tags, same classes, same attributes, same variant/modifier naming.
|
|
177
|
+
- Map Figma design tokens to project design tokens
|
|
178
|
+
- When a matching component exists, extend it rather than creating a new one
|
|
179
|
+
- Document any new components added to the design system
|
|
180
|
+
- If any workaround would change assets, mapped component contracts, markup structure, or visual composition in a way that is not explicitly present in Figma, STOP and get user approval first.
|
|
181
|
+
|
|
182
|
+
## Examples
|
|
183
|
+
|
|
184
|
+
### Example 1: Implementing a Button Component
|
|
185
|
+
|
|
186
|
+
User says: "Implement this Figma button component: https://figma.com/design/kL9xQn2VwM8pYrTb4ZcHjF/DesignSystem?node-id=42-15"
|
|
187
|
+
|
|
188
|
+
**Actions:**
|
|
189
|
+
|
|
190
|
+
1. Parse URL to extract fileKey=`kL9xQn2VwM8pYrTb4ZcHjF` and nodeId=`42-15`
|
|
191
|
+
2. Run `get_design_context(fileKey="kL9xQn2VwM8pYrTb4ZcHjF", nodeId="42-15")` and `get_code_connect_map(fileKey="kL9xQn2VwM8pYrTb4ZcHjF", nodeId="42-15")`
|
|
192
|
+
3. Run `get_screenshot(fileKey="kL9xQn2VwM8pYrTb4ZcHjF", nodeId="42-15")` for visual reference
|
|
193
|
+
4. Download any button icons from the assets endpoint
|
|
194
|
+
5. Check if project has existing button component
|
|
195
|
+
6. If yes, extend it with new variant; if no, create new component using project conventions
|
|
196
|
+
7. Map Figma colors to project design tokens (e.g., `primary-500`, `primary-hover`)
|
|
197
|
+
8. Validate against screenshot for padding, border radius, typography
|
|
198
|
+
|
|
199
|
+
**Result:** Button component matching Figma design, integrated with project design system.
|
|
200
|
+
|
|
201
|
+
### Example 2: Building a Dashboard Layout
|
|
202
|
+
|
|
203
|
+
User says: "Build this dashboard: https://figma.com/design/pR8mNv5KqXzGwY2JtCfL4D/Dashboard?node-id=10-5"
|
|
204
|
+
|
|
205
|
+
**Actions:**
|
|
206
|
+
|
|
207
|
+
1. Parse URL to extract fileKey=`pR8mNv5KqXzGwY2JtCfL4D` and nodeId=`10-5`
|
|
208
|
+
2. Run `get_metadata(fileKey="pR8mNv5KqXzGwY2JtCfL4D", nodeId="10-5")` to understand the page structure
|
|
209
|
+
3. Identify main sections from metadata (header, sidebar, content area, cards) and their child node IDs
|
|
210
|
+
4. Run `get_design_context(fileKey="pR8mNv5KqXzGwY2JtCfL4D", nodeId=":childNodeId")` and `get_code_connect_map(fileKey="pR8mNv5KqXzGwY2JtCfL4D", nodeId=":childNodeId")` for each major section
|
|
211
|
+
5. Run `get_screenshot(fileKey="pR8mNv5KqXzGwY2JtCfL4D", nodeId="10-5")` for the full page
|
|
212
|
+
6. Download all assets (logos, icons, charts)
|
|
213
|
+
7. Build layout using project's layout primitives
|
|
214
|
+
8. Implement each section using existing components where possible
|
|
215
|
+
9. Validate responsive behavior against Figma constraints
|
|
216
|
+
|
|
217
|
+
### Code Quality
|
|
218
|
+
|
|
219
|
+
- Avoid hardcoded values - extract to constants or design tokens
|
|
220
|
+
- Keep components composable and reusable
|
|
221
|
+
|
|
222
|
+
## Best Practices
|
|
223
|
+
|
|
224
|
+
### Always Start with Context
|
|
225
|
+
|
|
226
|
+
Never implement based on assumptions. Always fetch `get_design_context`, `get_code_connect_map` and `get_screenshot` first.
|
|
227
|
+
|
|
228
|
+
### Original Assets First
|
|
229
|
+
|
|
230
|
+
If Figma already provides an asset, vector, or exact SVG/code representation, use that original source. Do NOT redraw icons, invent substitute artwork, or replace provided assets with lookalikes.
|
|
231
|
+
|
|
232
|
+
### Incremental Validation
|
|
233
|
+
|
|
234
|
+
Validate frequently during implementation, not just at the end. This catches issues early.
|
|
235
|
+
|
|
236
|
+
### Ask Before Large Inventions
|
|
237
|
+
|
|
238
|
+
If the implementation required inventing significant structure, changing layout patterns, or making large visual/architectural decisions that were not explicitly requested, STOP and ask the user before proceeding.
|
|
239
|
+
|
|
240
|
+
### Reuse Over Recreation
|
|
241
|
+
|
|
242
|
+
Always check for existing components before creating new ones. Consistency across the codebase is more important than exact Figma replication.
|
|
243
|
+
|
|
244
|
+
### Design System First
|
|
245
|
+
|
|
246
|
+
When in doubt, prefer the project's design system patterns over literal Figma translation.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Figma Implement Design"
|
|
3
|
+
short_description: "Turn Figma designs into production-ready code"
|
|
4
|
+
icon_small: "./assets/figma-small.svg"
|
|
5
|
+
icon_large: "./assets/figma.png"
|
|
6
|
+
default_prompt: "Implement this Figma design in this codebase, using mapped Code Connect components wherever available and matching layout, states, and responsive behavior."
|
|
7
|
+
|
|
8
|
+
dependencies:
|
|
9
|
+
tools:
|
|
10
|
+
- type: "mcp"
|
|
11
|
+
value: "figma"
|
|
12
|
+
description: "Figma MCP server"
|
|
13
|
+
transport: "streamable_http"
|
|
14
|
+
url: "https://mcp.figma.com/mcp"
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
2
|
+
<path fill="#000" fill-rule="evenodd" d="M4.994 5.986a2.014 2.014 0 1 0 0 4.028h2.069V5.986H4.994Zm5.063-.98h.055a2.014 2.014 0 1 0 0-4.026h-2.07v4.027h2.015Zm1.697.49A2.994 2.994 0 0 0 10.112 0H4.994a2.994 2.994 0 0 0-1.642 5.498A2.99 2.99 0 0 0 2 8a2.99 2.99 0 0 0 1.352 2.503A2.99 2.99 0 0 0 2 13.007C2 14.663 3.358 16 5.008 16c1.665 0 3.035-1.349 3.035-3.02v-2.765a2.984 2.984 0 0 0 2.014.778h.055a2.994 2.994 0 0 0 1.642-5.496Zm-1.642.49h-.055a2.014 2.014 0 1 0 0 4.028h.055a2.014 2.014 0 1 0 0-4.028Zm-7.132 7.02c0-1.111.902-2.013 2.014-2.013h2.069v1.987c0 1.123-.924 2.04-2.055 2.04a2.026 2.026 0 0 1-2.028-2.013Zm4.083-8H4.994a2.014 2.014 0 1 1 0-4.026h2.069v4.027Z" clip-rule="evenodd"/>
|
|
3
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
width="400"
|
|
3
|
+
height="400"
|
|
4
|
+
viewBox="0 0 400 400"
|
|
5
|
+
fill="none"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
>
|
|
8
|
+
<path
|
|
9
|
+
d="M97.5 302.5C97.5 274.195 120.445 251.25 148.75 251.25H200V302.5C200 330.805 177.055 353.75 148.75 353.75C120.445 353.75 97.5 330.805 97.5 302.5Z"
|
|
10
|
+
fill="#0ACF83"
|
|
11
|
+
/>
|
|
12
|
+
<path
|
|
13
|
+
d="M200 200C200 171.696 222.945 148.75 251.25 148.75C279.554 148.75 302.5 171.695 302.5 200C302.5 228.305 279.554 251.25 251.25 251.25C222.945 251.25 200 228.304 200 200Z"
|
|
14
|
+
fill="#1ABCFE"
|
|
15
|
+
/>
|
|
16
|
+
<path
|
|
17
|
+
d="M97.5 200C97.5 228.305 120.445 251.25 148.75 251.25H200V148.75H148.75C120.445 148.75 97.5 171.695 97.5 200Z"
|
|
18
|
+
fill="#A259FF"
|
|
19
|
+
/>
|
|
20
|
+
<path
|
|
21
|
+
d="M200 46.25V148.75H251.25C279.555 148.75 302.5 125.805 302.5 97.5C302.5 69.1954 279.555 46.25 251.25 46.25H200Z"
|
|
22
|
+
fill="#FF7262"
|
|
23
|
+
/>
|
|
24
|
+
<path
|
|
25
|
+
d="M97.5 97.5C97.5 125.805 120.445 148.75 148.75 148.75H200V46.25L148.75 46.25C120.445 46.25 97.5 69.1954 97.5 97.5Z"
|
|
26
|
+
fill="#F24E1E"
|
|
27
|
+
/>
|
|
28
|
+
</svg>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: newlogic-ui
|
|
3
|
+
description: This skill provides repo-specific rules for LLM agents working with the Newlogic UI framework. Use it for UI, components, layouts, styles, responsiveness, frontend logic, and design implementation in this repository.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Newlogic UI Skill
|
|
7
|
+
|
|
8
|
+
ALWAYS load `https://ui.newlogic.cz/llms-full.txt` before making changes in this framework.
|
|
9
|
+
|
|
10
|
+
ALWAYS read ALL local guides in `references/` before starting work. These guides are intentionally short and contain ONLY repo-specific rules that override, narrow, or clarify `llms-full.txt`.
|
|
11
|
+
|
|
12
|
+
## Source Priority
|
|
13
|
+
|
|
14
|
+
Use this priority order when rules differ:
|
|
15
|
+
|
|
16
|
+
1. Existing files in this repository
|
|
17
|
+
2. Local guides in this skill
|
|
18
|
+
3. `https://ui.newlogic.cz/llms-full.txt`
|
|
19
|
+
|
|
20
|
+
## Working Rule
|
|
21
|
+
|
|
22
|
+
Use `llms-full.txt` for framework behavior, available components, and implementation examples.
|
|
23
|
+
|
|
24
|
+
Use the local guides for repository conventions that MUST be followed even if a generic example from `llms-full.txt` suggests another valid approach.
|
|
25
|
+
|
|
26
|
+
## Required Guides
|
|
27
|
+
|
|
28
|
+
- [Project Guide](references/project-guide.md) - naming, structure, and placement rules
|
|
29
|
+
- [Assets Guide](references/assets-guide.md) - placeholder, image, and icon rules
|
|
30
|
+
- [Templating Guide](references/templating-guide.md) - Latte and JSON data rules for this repo
|
|
31
|
+
- [Styling Guide](references/styling-guide.md) - local CSS and component styling constraints
|
|
32
|
+
- [Theme Guide](references/theme-guide.md) - local theme token and theme file rules
|
|
33
|
+
- [Scripting Guide](references/scripting-guide.md) - local JS and Stimulus placement rules
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Assets Guide
|
|
2
|
+
|
|
3
|
+
## Images
|
|
4
|
+
|
|
5
|
+
- Do NOT use assets from Figma unless the user explicitly asks for them.
|
|
6
|
+
- Instead, use the placeholder images below.
|
|
7
|
+
- For placeholder images use https://placehold.co/ (preferred by default) or https://picsum.photos/.
|
|
8
|
+
- NEVER reinvent or reconstruct the images from scratch. Either use a placeholder image as `img` or a Figma asset as `img`.
|
|
9
|
+
- Most content images SHOULD use the `x-image` wrapper.
|
|
10
|
+
- Put the aspect ratio utility on the `<img>` element, NOT on the wrapper.
|
|
11
|
+
- Transparent images SHOULD NOT use the `x-image` wrapper.
|
|
12
|
+
|
|
13
|
+
**Preferred markup:**
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<div class="x-image before:skeleton">
|
|
17
|
+
<img class="aspect-4/3" src="https://placehold.co/800x600" loading="lazy" alt="">
|
|
18
|
+
</div>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Transparent image markup:**
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<img class="aspect-3/2" src="https://placehold.co/300x200" loading="lazy" alt="">
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Icons
|
|
28
|
+
|
|
29
|
+
- ALWAYS use SVG for icons.
|
|
30
|
+
- Prefer `<use>` for reusable icons.
|
|
31
|
+
- If adding reusable outline icons, place them in `src/icons/outline/`.
|
|
32
|
+
- If adding reusable solid icons, place them in `src/icons/solid/`.
|
|
33
|
+
- Brand icons are available in `src/icons/simpleicons/`.
|
|
34
|
+
- Heroicons are already available through the build setup, so do NOT duplicate them in `src/icons/`.
|
|
35
|
+
- The `<use href="...">` symbol definitions are generated automatically by the build setup.
|
|
36
|
+
- Icon IDs SHOULD generally follow Figma naming when possible.
|
|
37
|
+
- Inline `<svg>` without `<use>` is allowed ONLY for one-off icons that will NOT be reused.
|
|
38
|
+
|
|
39
|
+
**Reusable icon examples:**
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<svg class="size-6">
|
|
43
|
+
<use href="#icons-outline/hello"></use>
|
|
44
|
+
</svg>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<svg class="size-6">
|
|
49
|
+
<use href="#simpleicons-solid/facebook"></use>
|
|
50
|
+
</svg>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<svg class="size-6">
|
|
55
|
+
<use href="#heroicons-outline/academic-cap"></use>
|
|
56
|
+
</svg>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Icon Strategy Summary
|
|
60
|
+
|
|
61
|
+
1. Heroicons via `<use href="#heroicons-*/*">`
|
|
62
|
+
2. Project reusable icons via `src/icons/outline`, `src/icons/solid`, or `src/icons/simpleicons`
|
|
63
|
+
3. Inline `<svg>` without `<use>` for one-off icons ONLY
|
|
64
|
+
|
|
65
|
+
## Example
|
|
66
|
+
You are about to translate the Figma output into this project.
|
|
67
|
+
|
|
68
|
+
### Step 1 – Figma handoff
|
|
69
|
+
- Figma output provide provides assets like this `<img alt="" className="absolute block max-w-none size-full" src={imgVector}`
|
|
70
|
+
- with urls like this https://www.figma.com/api/mcp/asset/557d8c1d-efe3-42ad-bccb-e5db8ebd14e6
|
|
71
|
+
- You succefully identified a type of the asset as `image` or `icon` from the Figma output.
|
|
72
|
+
|
|
73
|
+
### Step 2 – Image
|
|
74
|
+
- You check the width and height of the image and decide if it is transparent or not.
|
|
75
|
+
|
|
76
|
+
#### If the user didn't ask to use Figma assets
|
|
77
|
+
- In this case you will use the placeholder image.
|
|
78
|
+
- You will use the correct width and height and aspect ratio.
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<div class="x-image before:skeleton">
|
|
82
|
+
<img class="aspect-4/3" src="https://placehold.co/800x600" loading="lazy" alt="">
|
|
83
|
+
</div>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- If the image is transparent, you will use the following markup without the `x-image` wrapper:
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<img class="aspect-3/2" src="https://placehold.co/300x200" loading="lazy" alt="">
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### If the user explicitly asks to use Figma assets
|
|
93
|
+
|
|
94
|
+
- You downloaded the image from Figma and placed it in `src/assets/images`.
|
|
95
|
+
- You renamed the image to match the Figma asset name.
|
|
96
|
+
- You updated the markup to use the new image path.
|
|
97
|
+
- You will use the correct width and height and aspect ratio.
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<div class="x-image before:skeleton">
|
|
101
|
+
<img class="aspect-4/3" src="/src/assets/my-new-image.jpg" loading="lazy" alt="">
|
|
102
|
+
</div>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Step 2 – Icon
|
|
106
|
+
- You check the width and height of the icon and decide if it is a brand icon, heroicon or other icon.
|
|
107
|
+
|
|
108
|
+
- If you identify the icon as a brand icon, you will check if it is already available in `src/icons/simpleicons`.
|
|
109
|
+
- If it is not available, you will add it to `src/icons/simpleicons`.
|
|
110
|
+
- If it is available, you will use the existing icon.
|
|
111
|
+
- You will use the following markup in the HTML:
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<svg class="size-6">
|
|
115
|
+
<use href="#simpleicons-solid/facebook"></use>
|
|
116
|
+
</svg>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- If you identify the icon as a heroicon, you will match the figma name to the heroicon name.
|
|
120
|
+
- You will use the following markup in the HTML:
|
|
121
|
+
```html
|
|
122
|
+
<svg class="size-6">
|
|
123
|
+
<use href="#heroicons-outline/academic-cap"></use>
|
|
124
|
+
</svg>
|
|
125
|
+
```
|
|
126
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Project Guide
|
|
2
|
+
|
|
3
|
+
## Naming
|
|
4
|
+
|
|
5
|
+
- Use PascalCase for component files such as `Button.latte`, `HeroSection.latte`, `Reveal.js`, `Header.css`.
|
|
6
|
+
- Use kebab-case with `x-` prefix for component classes such as `x-button`, `x-hero-section`.
|
|
7
|
+
- ALWAYS check if the component class name matches the file name, e.g. `HeroSection.latte` and `x-hero-section` matches
|
|
8
|
+
- ALWAYS keep one primary `x-*` component per file.
|
|
9
|
+
- Use kebab-case for generic utility files such as `format-date.js` or `scrollbar.css`.
|
|
10
|
+
|
|
11
|
+
## Placement
|
|
12
|
+
|
|
13
|
+
- Latte components belong in `src/templates/components/`.
|
|
14
|
+
- UI component templates belong in `src/templates/components/(ui)/` when they are reusable UI building blocks.
|
|
15
|
+
- Section or layout groupings MAY use parentheses directories such as `(sections)` or `(layout)`.
|
|
16
|
+
- Component CSS belongs in `src/styles/components/`.
|
|
17
|
+
- Winduum-based UI component CSS belongs in `src/styles/components/(ui)/`.
|
|
18
|
+
- Component scripts belong in `src/scripts/components/`.
|
|
19
|
+
- Winduum-based UI controllers belong in `src/scripts/components/(ui)/`.
|
|
20
|
+
|
|
21
|
+
## Structure
|
|
22
|
+
|
|
23
|
+
- Match template, style, and script names when the same component exists across layers.
|
|
24
|
+
- Keep the repo structure aligned with existing files before introducing a new folder or naming pattern.
|
|
25
|
+
- If a pattern already exists in nearby files, ALWAYS follow that pattern instead of inventing a parallel one.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Scripting Guide
|
|
2
|
+
|
|
3
|
+
## Placement
|
|
4
|
+
|
|
5
|
+
- Entry point is `src/scripts/main.js`.
|
|
6
|
+
- Shared setup belongs in `src/scripts/composables/`.
|
|
7
|
+
- Reusable helpers belong in `src/scripts/utils/`.
|
|
8
|
+
- Custom component controllers belong in `src/scripts/components/`.
|
|
9
|
+
- Winduum-based UI controllers belong in `src/scripts/components/(ui)/`.
|
|
10
|
+
|
|
11
|
+
## Local Rules
|
|
12
|
+
|
|
13
|
+
- Use modern plain JS that matches the existing codebase.
|
|
14
|
+
- Register one primary controller per file.
|
|
15
|
+
- Keep controller names aligned with existing `x-*` naming in this repo.
|
|
16
|
+
- If dynamic HTML is inserted into the DOM, ALWAYS re-initialize it with `src/scripts/utils/initAfter.js`.
|
|
17
|
+
- Follow the existing import aggregation pattern with `+.js` files when adding new controllers to an existing folder.
|
|
18
|
+
|
|
19
|
+
**Local `initAfter` import pattern:**
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import { initAfter } from '../utils/+.js'
|
|
23
|
+
|
|
24
|
+
initAfter(document.querySelector('.newlyAppendedContent'))
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Aggregator pattern:**
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// src/scripts/components/(ui)/+.js
|
|
31
|
+
import './Button.js'
|
|
32
|
+
import './Dialog.js'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Before Adding JS
|
|
36
|
+
|
|
37
|
+
- Prefer existing Winduum, Stimulus, or utility behavior from `llms-full.txt` before creating new custom JS.
|
|
38
|
+
- Do NOT introduce a second interaction pattern if the repo already has an established controller for the same problem.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Styling Guide
|
|
2
|
+
|
|
3
|
+
## General
|
|
4
|
+
|
|
5
|
+
- ALWAYS prefer Tailwind utilities before writing custom CSS.
|
|
6
|
+
- Use custom CSS for defaults, complex states, or component-specific behavior that should NOT live in templates.
|
|
7
|
+
- Follow nearby files before introducing a new styling pattern.
|
|
8
|
+
- Prefer responsive utilities such as `sm:`, `md:`, and `lg:` in templates. Use `@custom-media` only when writing custom CSS.
|
|
9
|
+
|
|
10
|
+
**Custom breakpoint example:**
|
|
11
|
+
|
|
12
|
+
```css
|
|
13
|
+
.my-component {
|
|
14
|
+
padding: 1rem;
|
|
15
|
+
|
|
16
|
+
@media (--media-lg) {
|
|
17
|
+
padding: 2rem;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## UI Components
|
|
23
|
+
|
|
24
|
+
- Winduum-based UI components live in `src/styles/components/(ui)/`.
|
|
25
|
+
- UI component defaults belong in their CSS files, NOT in template utility piles.
|
|
26
|
+
- Define base sizing, padding, radius, font defaults, and default colors in CSS for UI components.
|
|
27
|
+
- In templates, only small layout utilities are allowed on UI components, such as width, flex, gap, order, or spacing adjustments.
|
|
28
|
+
- Do NOT redefine a UI component's base look directly in templates.
|
|
29
|
+
- Do NOT restyle existing `x-*` UI components from a large level CSS file when the change is really a button, badge, control, field, pagination, or other UI-component concern.
|
|
30
|
+
- If a Figma page introduces a new shared UI look, extend the relevant file in `src/styles/components/(ui)/` instead of creating specific overrides from a parent component for that component.
|
|
31
|
+
|
|
32
|
+
## Non-UI Components
|
|
33
|
+
|
|
34
|
+
- Regular non-UI components SHOULD be styled entirely with Tailwind utilities in the template by default.
|
|
35
|
+
- For non-UI components, prefer Tailwind even when the template gets longer. A longer utility-based template is still preferred over creating component CSS too early.
|
|
36
|
+
- Create component CSS for non-UI components ONLY when there is styling logic that genuinely should not live in the template, such as complex selectors, state relationships, pseudo-element composition, browser-specific behavior, or similarly non-trivial logic that cannot be expressed cleanly with Tailwind utilities alone.
|
|
37
|
+
- Do NOT create component CSS just to group utilities, shorten markup, or move ordinary layout/spacing/typography rules out of the template.
|
|
38
|
+
- Treat non-UI component CSS as a true last resort. It should be used only in genuinely exceptional cases, not as a convenience or organizational preference.
|
|
39
|
+
- If a non-UI component reaches that exceptional threshold and needs CSS, follow the `data-part` pattern for internal styling instead of inventing extra internal class names.
|
|
40
|
+
- Avoid large level CSS files that absorb layout, typography, and UI and other component styling at the same time. If a page file starts becoming the source of truth for `x-*` components, move that logic back into `(ui)` CSS and keep the page mostly utility-driven.
|
|
41
|
+
|
|
42
|
+
## Tokens And Units
|
|
43
|
+
|
|
44
|
+
- ALWAYS use theme tokens such as `text-primary`, `bg-main`, or `text-body-foreground` before raw color values.
|
|
45
|
+
- If the design needs a new default token, update `src/styles/theme/main.css`.
|
|
46
|
+
- Do NOT use `px` units, except `1px` or `2px` borders when needed.
|
|
47
|
+
|
|
48
|
+
## Component Parts
|
|
49
|
+
|
|
50
|
+
- This section applies primarily to non-UI components when CSS is truly unavoidable.
|
|
51
|
+
- Use `data-part` for stylable internal parts.
|
|
52
|
+
- Do NOT create extra part class names such as `x-card-header` or `x-hero-content` when `data-part` is sufficient.
|
|
53
|
+
- For non-UI components, `data-part` is the required internal styling pattern whenever an exceptional CSS case is justified.
|
|
54
|
+
|
|
55
|
+
**Preferred part pattern:**
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<div class="x-my-component">
|
|
59
|
+
<div data-part="header">...</div>
|
|
60
|
+
<div data-part="content">...</div>
|
|
61
|
+
</div>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```css
|
|
65
|
+
.x-my-component {
|
|
66
|
+
[data-part="header"] {
|
|
67
|
+
/* complex styles */
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Winduum Extensions
|
|
73
|
+
|
|
74
|
+
- Inspect the imported Winduum files in the component CSS to see which custom properties are available.
|
|
75
|
+
- Extend Winduum components through their existing imports and local CSS layers instead of re-implementing them from scratch.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Templating Guide
|
|
2
|
+
|
|
3
|
+
## Layout
|
|
4
|
+
|
|
5
|
+
- Use `src/templates/layouts/default.latte` unless the user explicitly asks for another layout.
|
|
6
|
+
- Do NOT create a custom layout when the default layout already supports the change.
|
|
7
|
+
|
|
8
|
+
## Data Sources
|
|
9
|
+
|
|
10
|
+
- Global data lives in `src/data/main.json`.
|
|
11
|
+
- In this repo, global layout sections are typically defined through `head` and `foot` arrays in `src/data/main.json`.
|
|
12
|
+
- Page data lives in `src/pages/*.json` and inherits global data.
|
|
13
|
+
- Section rendering is handled through `src/templates/utils/sections.latte`.
|
|
14
|
+
- Components referenced from page JSON are passed as `$control`.
|
|
15
|
+
- In this repo, `src/pages/*.json` is the composition layer for whole pages. Prefer assembling a page there instead of creating a wrapper `*Page.latte` component that just nests the entire page structure.
|
|
16
|
+
- Preserve the existing `head` and `foot` layout structure unless the user explicitly asks to change the layout contract.
|
|
17
|
+
- If a Figma page includes a site header or site footer, implement those designs in the existing `components/header/Header.latte` and `components/footer/Footer.latte` flow rather than moving them into `body`.
|
|
18
|
+
|
|
19
|
+
**Global layout data example:**
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"head": [
|
|
24
|
+
{ "src": "components/header/Header.latte" }
|
|
25
|
+
],
|
|
26
|
+
"foot": [
|
|
27
|
+
{ "src": "components/footer/Footer.latte" }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Layout asset access example:**
|
|
33
|
+
|
|
34
|
+
```latte
|
|
35
|
+
<link n:foreach="$assets->css->all as $url" href="{$url|asset}" rel="stylesheet">
|
|
36
|
+
<script src="{$assets->js->main|asset}" type="module"></script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## JSON Rules
|
|
40
|
+
|
|
41
|
+
- Do NOT store CSS class strings in JSON.
|
|
42
|
+
- JSON should contain semantic values and content, NOT presentation classes.
|
|
43
|
+
- If a template depends on an optional flag such as `active`, `dropdown`, or variant switches, define that flag explicitly in JSON.
|
|
44
|
+
- Keep repeated global UI data in `src/data/main.json` rather than duplicating it across page JSON files.
|
|
45
|
+
|
|
46
|
+
## Latte Rules
|
|
47
|
+
|
|
48
|
+
- Prefer direct access to existing variables such as `$control` and global layout data.
|
|
49
|
+
- Keep ad hoc `{var ...}` usage to a minimum.
|
|
50
|
+
- Use `{default}` for optional fallback values when needed.
|
|
51
|
+
- Do NOT combine `class` and `n:class` on the same element. Use one `n:class` expression that includes the base classes.
|
|
52
|
+
|
|
53
|
+
**`n:class` pattern:**
|
|
54
|
+
|
|
55
|
+
```latte
|
|
56
|
+
<a n:class="'base classes ' . ($isActive ? 'active' : 'idle')">Link</a>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**`{default}` pattern:**
|
|
60
|
+
|
|
61
|
+
```latte
|
|
62
|
+
{default $disabled = false}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Components
|
|
66
|
+
|
|
67
|
+
- Put reusable page sections in `src/templates/components/`.
|
|
68
|
+
- Keep component file names in PascalCase.
|
|
69
|
+
- If a component is rendered from page JSON, assume `$control` is the primary input object unless the existing local pattern clearly does something else.
|
|
70
|
+
- Match the current folder conventions such as `(sections)`, `(ui)`, `header/`, `footer/`, `dialog/`, or `cookieconsent/` before introducing a new grouping.
|
|
71
|
+
- Do NOT create a monolithic page wrapper component when the page can be expressed as multiple body sections in JSON.
|
|
72
|
+
- Prefer one component per visible section or repeated block, then compose those sections from `src/pages/*.json`.
|
|
73
|
+
- If an existing component already matches the Figma element, reuse that component instead of creating a specific duplicate.
|
|
74
|
+
- If a Figma element maps to an existing `(ui)` component such as `Pagination.latte`, `Button`, `Dialog`, `Toast`, or similar, extend that existing component if needed; do NOT create parallel specific versions like `ArticlesPagination.latte` unless the user explicitly asks for a separate component.
|
|
75
|
+
|
|
76
|
+
**Page JSON to component flow:**
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"body": [
|
|
81
|
+
{
|
|
82
|
+
"src": "components/HeroSection.latte",
|
|
83
|
+
"heading": "Welcome",
|
|
84
|
+
"buttonText": "Get Started"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```latte
|
|
91
|
+
{foreach $sections as $section}
|
|
92
|
+
{include ('../' . $section->src), control => $section}
|
|
93
|
+
{/foreach}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```latte
|
|
97
|
+
<section class="x-hero-section">
|
|
98
|
+
<h1 n:if="isset($control->heading)">{$control->heading}</h1>
|
|
99
|
+
</section>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Direct include pattern:**
|
|
103
|
+
|
|
104
|
+
```latte
|
|
105
|
+
{include 'components/Button.latte', text: 'Click me'}
|
|
106
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Theme Guide
|
|
2
|
+
|
|
3
|
+
## Theme Files
|
|
4
|
+
|
|
5
|
+
- Main website theme tokens live in `src/styles/theme/main.css`.
|
|
6
|
+
- Keep website theme defaults in `@theme`.
|
|
7
|
+
- Extend the existing token system before inventing component-local hardcoded values.
|
|
8
|
+
|
|
9
|
+
## Local Rules
|
|
10
|
+
|
|
11
|
+
- ALWAYS prefer semantic tokens over raw values in templates and components.
|
|
12
|
+
- If the design introduces a new shared color, spacing, or layout default, add or adjust the token in `src/styles/theme/main.css`.
|
|
13
|
+
- Reuse existing tokens such as `primary`, `main`, `body`, and status colors before creating new ones.
|
|
14
|
+
- Keep theme changes centralized instead of scattering hardcoded values across multiple component files.
|
|
15
|
+
|
|
16
|
+
**Common token usage:**
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<div class="bg-primary text-primary-foreground">Primary Button</div>
|
|
20
|
+
<div class="bg-main text-main-foreground">Dark Section</div>
|
|
21
|
+
<div class="max-w-(--container-width)">Constrained content</div>
|
|
22
|
+
<div class="x-button accent-main">Primary text</div>
|
|
23
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { styleText } from 'node:util'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { getPackageInfo } from 'vituum/utils/common.js'
|
|
6
|
+
|
|
7
|
+
const IS_WIN32 = process.platform === 'win32'
|
|
8
|
+
const { name } = getPackageInfo(new URL('../package.json', import.meta.url).href)
|
|
9
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const packageRoot = path.resolve(scriptDir, '..')
|
|
11
|
+
const sourceSkillsDir = path.join(packageRoot, 'skills')
|
|
12
|
+
const projectRoot = process.env.INIT_CWD ? path.resolve(process.env.INIT_CWD) : null
|
|
13
|
+
|
|
14
|
+
function isSameSymlinkTarget(targetPath, expectedSourcePath) {
|
|
15
|
+
const currentTarget = fs.readlinkSync(targetPath)
|
|
16
|
+
const resolvedTarget = path.resolve(path.dirname(targetPath), currentTarget)
|
|
17
|
+
|
|
18
|
+
return resolvedTarget === expectedSourcePath
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getExistingTargetStat(targetPath) {
|
|
22
|
+
try {
|
|
23
|
+
return fs.lstatSync(targetPath)
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error?.code === 'ENOENT') {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
throw error
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createDirectorySymlink(sourcePath, targetPath) {
|
|
35
|
+
const symlinkTarget = IS_WIN32
|
|
36
|
+
? sourcePath
|
|
37
|
+
: path.relative(path.dirname(targetPath), sourcePath)
|
|
38
|
+
const symlinkType = IS_WIN32 ? 'junction' : 'dir'
|
|
39
|
+
|
|
40
|
+
fs.symlinkSync(symlinkTarget, targetPath, symlinkType)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!projectRoot || projectRoot === packageRoot || !fs.existsSync(sourceSkillsDir)) {
|
|
44
|
+
process.exit(0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const targetAgentsDir = path.join(projectRoot, '.agents')
|
|
48
|
+
const targetSkillsDir = path.join(targetAgentsDir, 'skills')
|
|
49
|
+
const targetClaudePath = path.join(projectRoot, '.claude')
|
|
50
|
+
const skillEntries = fs.readdirSync(sourceSkillsDir, { withFileTypes: true })
|
|
51
|
+
.filter(entry => entry.isDirectory())
|
|
52
|
+
|
|
53
|
+
if (skillEntries.length === 0) {
|
|
54
|
+
process.exit(0)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fs.mkdirSync(targetSkillsDir, { recursive: true })
|
|
58
|
+
|
|
59
|
+
let linkedCount = 0
|
|
60
|
+
let linkedClaudeDir = false
|
|
61
|
+
|
|
62
|
+
for (const entry of skillEntries) {
|
|
63
|
+
const sourcePath = path.join(sourceSkillsDir, entry.name)
|
|
64
|
+
const targetPath = path.join(targetSkillsDir, entry.name)
|
|
65
|
+
const targetStat = getExistingTargetStat(targetPath)
|
|
66
|
+
|
|
67
|
+
if (targetStat) {
|
|
68
|
+
if (!targetStat.isSymbolicLink()) {
|
|
69
|
+
console.warn(`${styleText(['cyan', 'bold'], name)} ${styleText('yellow', `Skipping skill "${entry.name}" because "${targetPath}" already exists and is not a symlink.`)}`)
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isSameSymlinkTarget(targetPath, sourcePath)) {
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fs.unlinkSync(targetPath)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
createDirectorySymlink(sourcePath, targetPath)
|
|
81
|
+
linkedCount += 1
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const claudeStat = getExistingTargetStat(targetClaudePath)
|
|
85
|
+
|
|
86
|
+
if (claudeStat) {
|
|
87
|
+
if (!claudeStat.isSymbolicLink()) {
|
|
88
|
+
console.warn(`${styleText(['cyan', 'bold'], name)} ${styleText('yellow', `Skipping ".claude" because "${targetClaudePath}" already exists and is not a symlink.`)}`)
|
|
89
|
+
}
|
|
90
|
+
else if (!isSameSymlinkTarget(targetClaudePath, targetAgentsDir)) {
|
|
91
|
+
fs.unlinkSync(targetClaudePath)
|
|
92
|
+
createDirectorySymlink(targetAgentsDir, targetClaudePath)
|
|
93
|
+
linkedClaudeDir = true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
createDirectorySymlink(targetAgentsDir, targetClaudePath)
|
|
98
|
+
linkedClaudeDir = true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (linkedCount > 0) {
|
|
102
|
+
console.info(`${styleText(['cyan', 'bold'], name)} ${styleText('green', `Linked ${linkedCount} skill${linkedCount === 1 ? '' : 's'} into "${targetSkillsDir}".`)}`)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (linkedClaudeDir) {
|
|
106
|
+
console.info(`${styleText(['cyan', 'bold'], name)} ${styleText('green', `Linked "${targetClaudePath}" to "${targetAgentsDir}".`)}`)
|
|
107
|
+
}
|