feat: Refactor mode switch component and enhance design tokens for improved UI consistency and functionality.

This commit is contained in:
Ho Ngoc Hai
2026-02-05 16:39:44 +07:00
parent 56a5185119
commit e83dcc0d1c
4 changed files with 639 additions and 314 deletions

View File

@@ -275,86 +275,23 @@ python3 scripts/build.py --library
## Common Mistakes / Lỗi Thường Gặp
### 1. Hardcoding Colors
**Problem**: Using hex codes directly in components (e.g. `#000000`), making theming impossible.
**Solution**: Always use design token variables.
> [!TIP]
> See [Pitfalls Reference](./references/PITFALLS.md) for detailed examples and solutions.
```css
/* ❌ BAD: Hardcoded colors */
background: #0A0A0B;
| # | Pitfall | Quick Fix |
|---|---------|-----------|
| 1 | Hardcoding colors | Use `$token` variables |
| 2 | Ignoring `ref` system | Resolve refs by component ID |
| 3 | Opening source files directly | Build first: `python3 scripts/build.py -m` |
| 4 | Direct hex values | Use `$orange-primary` not `#FF5C00` |
| 5 | Centering without width | Add `width: "fill_container"` |
| 6 | Font weight as number | Use `type: "string"`, `value: "500"` |
| 7 | Empty `variables: {}` | Define all used tokens |
| 8 | Token refs in fontSize | Use numbers directly: `fontSize: 14` |
| 9 | Emoji text for icons | Use `icon_font` with Lucide |
| 10 | `glob()` missing subdirs | Use `rglob()` for recursive scan |
| 11 | Components at root level | Wrap in Showcase frame with layout |
/* ✅ GOOD: Use CSS variables */
background: var(--bg-page);
```
### 2. Ignoring Referencing System
**Problem**: Treating `ref` elements as normal frames, losing link to main component.
**Solution**: Resolve `ref` by looking up the component ID.
```javascript
// ❌ BAD: Skip ref elements
if (element.type === 'ref') return;
// ✅ GOOD: Resolve ref properly
if (element.type === 'ref') {
const component = findById(element.ref);
return mergeWithDescendants(component, element.descendants);
}
```
### 3. Modifying Source Files Directly
**Problem**: Open and editing partial files in `src/` without building, causing "Missing Component" errors in Pencil.
**Solution**: Always run build script before opening in Pencil.
```bash
# ❌ BAD: Open source files directly
open src/pages/desktop-home.pen
# ✅ GOOD: Build first
python3 scripts/build.py -m
open tPOS.pen
```
### 4. Direct Value Usage
**Problem**: Using numeric values for spacing/fontsize, breaking design system consistency.
**Solution**: Use semantic variables.
```json
// ❌ BAD: Direct values
{
"fill": "#FF5C00",
"fontSize": 16
}
// ✅ GOOD: Use variables
{
"fill": "$orange-primary",
"fontSize": "$text-lg"
}
```
### 5. Missing Width for Centering
**Problem**: Setting `alignItems: "center"` without explicit width. The frame only takes content width, making horizontal centering ineffective.
**Solution**: Add `width: "fill_container"` to parent frame.
```json
// ❌ BAD: Centering won't work
{
"type": "frame",
"layout": "vertical",
"alignItems": "center",
"children": [...]
}
// ✅ GOOD: Explicit width enables centering
{
"type": "frame",
"width": "fill_container",
"layout": "vertical",
"alignItems": "center",
"children": [...]
}
```
## Quick Reference / Tham Chiếu Nhanh

View File

@@ -0,0 +1,315 @@
# Pencil Design Pitfalls / Lỗi Thường Gặp
Comprehensive list of common mistakes when working with Pencil `.pen` files and their solutions.
## Table of Contents
1. [Hardcoding Colors](#1-hardcoding-colors)
2. [Ignoring Referencing System](#2-ignoring-referencing-system)
3. [Modifying Source Files Directly](#3-modifying-source-files-directly)
4. [Direct Value Usage](#4-direct-value-usage)
5. [Missing Width for Centering](#5-missing-width-for-centering)
6. [Font Weight Variables Type Error](#6-font-weight-variables-type-error)
7. [Empty Variables Block](#7-empty-variables-block)
8. [Token References in Numeric Fields](#8-token-references-in-numeric-fields)
9. [Using Emoji Text for Icons](#9-using-emoji-text-for-icons)
10. [Build Script Not Scanning Subdirectories](#10-build-script-not-scanning-subdirectories)
11. [Components at Root Level](#11-components-at-root-level)
---
## 1. Hardcoding Colors
**Problem**: Using hex codes directly in components (e.g. `#000000`), making theming impossible.
**Solution**: Always use design token variables.
```css
/* ❌ BAD: Hardcoded colors */
background: #0A0A0B;
/* ✅ GOOD: Use CSS variables */
background: var(--bg-page);
```
---
## 2. Ignoring Referencing System
**Problem**: Treating `ref` elements as normal frames, losing link to main component.
**Solution**: Resolve `ref` by looking up the component ID.
```javascript
// ❌ BAD: Skip ref elements
if (element.type === 'ref') return;
// ✅ GOOD: Resolve ref properly
if (element.type === 'ref') {
const component = findById(element.ref);
return mergeWithDescendants(component, element.descendants);
}
```
---
## 3. Modifying Source Files Directly
**Problem**: Opening and editing partial files in `src/` without building, causing "Missing Component" errors in Pencil.
**Solution**: Always run build script before opening in Pencil.
```bash
# ❌ BAD: Open source files directly
open src/pages/desktop-home.pen
# ✅ GOOD: Build first
python3 scripts/build.py -m
open tPOS.pen
```
---
## 4. Direct Value Usage
**Problem**: Using numeric values for spacing/fontsize, breaking design system consistency.
**Solution**: Use semantic variables (for colors only - see #8).
```json
// ❌ BAD: Direct color values
{
"fill": "#FF5C00"
}
// ✅ GOOD: Use color variables
{
"fill": "$orange-primary"
}
```
---
## 5. Missing Width for Centering
**Problem**: Setting `alignItems: "center"` without explicit width. The frame only takes content width, making horizontal centering ineffective.
**Solution**: Add `width: "fill_container"` to parent frame.
```json
// ❌ BAD: Centering won't work
{
"type": "frame",
"layout": "vertical",
"alignItems": "center",
"children": [...]
}
// ✅ GOOD: Explicit width enables centering
{
"type": "frame",
"width": "fill_container",
"layout": "vertical",
"alignItems": "center",
"children": [...]
}
```
---
## 6. Font Weight Variables Type Error
**Problem**: Defining font-weight variables with `type: "number"` causes Pencil to fail with:
```
Variable 'font-medium' has type 'number' (expected 'string')
```
**Solution**: Font weights MUST use `type: "string"` with quoted values.
```json
// ❌ BAD: Number type
"font-medium": { "type": "number", "value": 500 }
// ✅ GOOD: String type with quoted value
"font-medium": { "type": "string", "value": "500" }
```
**All font weight variables to fix:**
```json
"font-normal": { "type": "string", "value": "400" },
"font-medium": { "type": "string", "value": "500" },
"font-semibold": { "type": "string", "value": "600" },
"font-bold": { "type": "string", "value": "700" }
```
---
## 7. Empty Variables Block
**Problem**: Using token references like `$orange-primary` but leaving `variables: {}` empty. Tokens won't resolve and colors appear wrong.
**Solution**: Always define all used tokens in the file's `variables` block.
```json
// ❌ BAD: Empty variables but using $tokens
{
"children": [{ "fill": "$orange-primary" }],
"variables": {}
}
// ✅ GOOD: Define all used tokens
{
"children": [{ "fill": "$orange-primary" }],
"variables": {
"orange-primary": { "type": "color", "value": "#FF5C00" }
}
}
```
---
## 8. Token References in Numeric Fields
**Problem**: Using token syntax (`$text-sm`) for properties that MUST be numbers: `fontSize`, `fontWeight`, `cornerRadius`, `width`, `height`.
**Solution**: Use actual numbers for numeric properties. Only colors support token refs.
```json
// ❌ BAD: Token in fontSize (won't work)
{
"fontSize": "$text-sm",
"fontWeight": "$font-medium",
"cornerRadius": "$radius-md"
}
// ✅ GOOD: Direct numeric values
{
"fontSize": 14,
"fontWeight": "500",
"cornerRadius": 8
}
// ✅ ALSO OK: Tokens for colors only
{ "fill": "$text-primary" }
```
**Rule of thumb:**
- Color properties (`fill`, `stroke`) → Use `$tokens`
- Numeric properties → Use numbers directly
---
## 9. Using Emoji Text for Icons
**Problem**: Emoji characters in `type: "text"` don't render in Pencil.
**Solution**: Use `type: "icon_font"` with Lucide icons.
```json
// ❌ BAD: Emoji text (won't render)
{ "type": "text", "content": "🍽️", "fontSize": 16 }
// ✅ GOOD: Lucide icon_font
{
"type": "icon_font",
"id": "RestaurantIcon",
"iconFontName": "utensils",
"iconFontFamily": "lucide",
"width": 16,
"height": 16,
"fill": "#FFFFFF"
}
```
**Common Lucide icon mappings:**
| Emoji | Lucide Name | Use Case |
|-------|-------------|----------|
| 🍽️ | `utensils` | Restaurant |
| 🍸 | `wine` | Bar |
| 🎤 | `mic` | Karaoke |
| 💆 | `sparkles` | Spa |
| ✓ | `check` | Success |
| ✗ | `x` | Close/Error |
| + | `plus` | Add |
| - | `minus` | Remove |
| 🔍 | `search` | Search |
| ⚙️ | `settings` | Settings |
| 👤 | `user` | Profile |
| 🏠 | `home` | Home |
| 💳 | `credit-card` | Payment |
| 🛒 | `shopping-cart` | Cart |
---
## 10. Build Script Not Scanning Subdirectories
**Problem**: Using `glob('*.pen')` only scans top-level files, missing subdirectories like `organisms/vertical-specific/`.
**Solution**: Use `rglob('*.pen')` to scan recursively.
```python
# ❌ BAD: Only top-level files
for pen_file in dir_path.glob('*.pen'):
# ✅ GOOD: Include subdirectories
for pen_file in dir_path.rglob('*.pen'):
```
**In `scripts/build.py`:**
```python
# Line ~367
for pen_file in sorted(dir_path.rglob('*.pen')):
```
---
## 11. Components at Root Level
**Problem**: Placing multiple reusable components at root `children[]` causes them to stack/overlap incorrectly in Pencil.
**Solution**: Wrap in a Showcase frame with layout.
```json
// ❌ BAD: Components at root level (will overlap)
{
"children": [
{ "name": "Component/A", "reusable": true },
{ "name": "Component/B", "reusable": true }
]
}
// ✅ GOOD: Wrapped in Showcase frame
{
"children": [
{
"type": "frame",
"name": "Component Showcase",
"width": 1200,
"fill": "$bg-page",
"layout": "vertical",
"gap": 40,
"padding": 40,
"children": [
{ "name": "Component/A", "reusable": true },
{ "name": "Component/B", "reusable": true }
]
}
]
}
```
---
## Quick Checklist / Checklist Nhanh
Before committing `.pen` files:
- [ ] All `font-*` variables use `type: "string"`
- [ ] `variables` block contains all used tokens
- [ ] No `$tokens` in numeric fields (fontSize, cornerRadius, etc.)
- [ ] Icons use `icon_font` not emoji text
- [ ] Build script uses `rglob()` for subdirectories
- [ ] Components wrapped in layout frames, not at root level
- [ ] Run `jq empty file.pen` to validate JSON syntax
- [ ] Run `python3 scripts/build.py --library` before testing