From e83dcc0d1c08738f5fde2b6f0c6cadf2f4b3e628 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Thu, 5 Feb 2026 16:39:44 +0700 Subject: [PATCH] feat: Refactor mode switch component and enhance design tokens for improved UI consistency and functionality. --- .agent/skills/pencil-design/SKILL.md | 93 +----- .../pencil-design/references/PITFALLS.md | 315 ++++++++++++++++++ pencil-design/src/components/tPOS-ui-kit.pen | 264 +++++++++------ pencil-design/src/molecules/mode-switch.pen | 281 +++++++++------- 4 files changed, 639 insertions(+), 314 deletions(-) create mode 100644 .agent/skills/pencil-design/references/PITFALLS.md diff --git a/.agent/skills/pencil-design/SKILL.md b/.agent/skills/pencil-design/SKILL.md index 640a766a..2cd5b3d2 100644 --- a/.agent/skills/pencil-design/SKILL.md +++ b/.agent/skills/pencil-design/SKILL.md @@ -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 diff --git a/.agent/skills/pencil-design/references/PITFALLS.md b/.agent/skills/pencil-design/references/PITFALLS.md new file mode 100644 index 00000000..d7a17d70 --- /dev/null +++ b/.agent/skills/pencil-design/references/PITFALLS.md @@ -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 diff --git a/pencil-design/src/components/tPOS-ui-kit.pen b/pencil-design/src/components/tPOS-ui-kit.pen index 2edf43f9..a7b75f42 100644 --- a/pencil-design/src/components/tPOS-ui-kit.pen +++ b/pencil-design/src/components/tPOS-ui-kit.pen @@ -951,10 +951,10 @@ }, { "type": "frame", - "id": "mode_switch_container", + "id": "ModeSwitchContainer", "name": "Molecule/ModeSwitch/Container", "reusable": true, - "width": "fill_container", + "width": 600, "height": 48, "layout": "horizontal", "justifyContent": "center", @@ -962,12 +962,12 @@ "gap": 4, "padding": 4, "fill": "$bg-surface", - "cornerRadius": "$radius-lg", + "cornerRadius": 12, "children": [ { "type": "frame", - "id": "mode_restaurant", - "name": "Mode/Restaurant/Active", + "id": "MSRestaurant", + "name": "modeRestaurantActive", "layout": "horizontal", "justifyContent": "center", "alignItems": "center", @@ -976,27 +976,33 @@ 8, 16 ], - "fill": "$vertical-restaurant", - "cornerRadius": "$radius-md", + "fill": "$orange-primary", + "cornerRadius": 8, "children": [ { - "type": "text", - "content": "\ud83c\udf7d\ufe0f", - "fontSize": 16 + "type": "icon_font", + "id": "MSRestIcon", + "iconFontName": "utensils", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "#FFFFFF" }, { "type": "text", + "id": "MSRestLabel", "content": "Nh\u00e0 h\u00e0ng", - "fontSize": "$text-sm", - "fontWeight": "$font-medium", + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", "fill": "#FFFFFF" } ] }, { "type": "frame", - "id": "mode_bar", - "name": "Mode/Bar/Inactive", + "id": "MSBar", + "name": "modeBarInactive", "layout": "horizontal", "justifyContent": "center", "alignItems": "center", @@ -1005,27 +1011,32 @@ 8, 16 ], - "fill": "transparent", - "cornerRadius": "$radius-md", + "cornerRadius": 8, "children": [ { - "type": "text", - "content": "\ud83c\udf78", - "fontSize": 16 + "type": "icon_font", + "id": "MSBarIcon", + "iconFontName": "wine", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "$text-secondary" }, { "type": "text", + "id": "MSBarLabel", "content": "Bar", - "fontSize": "$text-sm", - "fontWeight": "$font-medium", + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", "fill": "$text-secondary" } ] }, { "type": "frame", - "id": "mode_karaoke", - "name": "Mode/Karaoke/Inactive", + "id": "MSKaraoke", + "name": "modeKaraokeInactive", "layout": "horizontal", "justifyContent": "center", "alignItems": "center", @@ -1034,27 +1045,32 @@ 8, 16 ], - "fill": "transparent", - "cornerRadius": "$radius-md", + "cornerRadius": 8, "children": [ { - "type": "text", - "content": "\ud83c\udfa4", - "fontSize": 16 + "type": "icon_font", + "id": "MSKarIcon", + "iconFontName": "mic", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "$text-secondary" }, { "type": "text", + "id": "MSKarLabel", "content": "Karaoke", - "fontSize": "$text-sm", - "fontWeight": "$font-medium", + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", "fill": "$text-secondary" } ] }, { "type": "frame", - "id": "mode_spa", - "name": "Mode/Spa/Inactive", + "id": "MSSpa", + "name": "modeSpaInactive", "layout": "horizontal", "justifyContent": "center", "alignItems": "center", @@ -1063,19 +1079,24 @@ 8, 16 ], - "fill": "transparent", - "cornerRadius": "$radius-md", + "cornerRadius": 8, "children": [ { - "type": "text", - "content": "\ud83d\udc86", - "fontSize": 16 + "type": "icon_font", + "id": "MSSpaIcon", + "iconFontName": "sparkles", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "$text-secondary" }, { "type": "text", + "id": "MSSpaLabel", "content": "Spa", - "fontSize": "$text-sm", - "fontWeight": "$font-medium", + "fontFamily": "Roboto", + "fontSize": 14, + "fontWeight": "500", "fill": "$text-secondary" } ] @@ -1084,81 +1105,106 @@ }, { "type": "frame", - "id": "mode_indicator_001", - "name": "Atom/ModeIndicator/Restaurant", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-restaurant", - "cornerRadius": "$radius-full", + "id": "ModeIndicators", + "name": "modeIndicatorsRow", + "layout": "horizontal", + "gap": 16, "children": [ { - "type": "text", - "content": "\ud83c\udf7d\ufe0f", - "fontSize": 16 - } - ] - }, - { - "type": "frame", - "id": "mode_indicator_002", - "name": "Atom/ModeIndicator/Bar", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-bar", - "cornerRadius": "$radius-full", - "children": [ + "type": "frame", + "id": "ModeIndRestaurant", + "name": "Molecule/ModeIndicator/Restaurant", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$orange-primary", + "cornerRadius": 9999, + "children": [ + { + "type": "icon_font", + "id": "MIRestIcon", + "iconFontName": "utensils", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "#FFFFFF" + } + ] + }, { - "type": "text", - "content": "\ud83c\udf78", - "fontSize": 16 - } - ] - }, - { - "type": "frame", - "id": "mode_indicator_003", - "name": "Atom/ModeIndicator/Karaoke", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-karaoke", - "cornerRadius": "$radius-full", - "children": [ + "type": "frame", + "id": "ModeIndBar", + "name": "Molecule/ModeIndicator/Bar", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$bar-primary", + "cornerRadius": 9999, + "children": [ + { + "type": "icon_font", + "id": "MIBarIcon", + "iconFontName": "wine", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "#FFFFFF" + } + ] + }, { - "type": "text", - "content": "\ud83c\udfa4", - "fontSize": 16 - } - ] - }, - { - "type": "frame", - "id": "mode_indicator_004", - "name": "Atom/ModeIndicator/Spa", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-spa", - "cornerRadius": "$radius-full", - "children": [ + "type": "frame", + "id": "ModeIndKaraoke", + "name": "Molecule/ModeIndicator/Karaoke", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$karaoke-primary", + "cornerRadius": 9999, + "children": [ + { + "type": "icon_font", + "id": "MIKarIcon", + "iconFontName": "mic", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "#FFFFFF" + } + ] + }, { - "type": "text", - "content": "\ud83d\udc86", - "fontSize": 16 + "type": "frame", + "id": "ModeIndSpa", + "name": "Molecule/ModeIndicator/Spa", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$spa-primary", + "cornerRadius": 9999, + "children": [ + { + "type": "icon_font", + "id": "MISpaIcon", + "iconFontName": "sparkles", + "iconFontFamily": "lucide", + "width": 16, + "height": 16, + "fill": "#FFFFFF" + } + ] } ] }, diff --git a/pencil-design/src/molecules/mode-switch.pen b/pencil-design/src/molecules/mode-switch.pen index 5667a017..20e5751b 100644 --- a/pencil-design/src/molecules/mode-switch.pen +++ b/pencil-design/src/molecules/mode-switch.pen @@ -3,149 +3,176 @@ "children": [ { "type": "frame", - "id": "mode_switch_container", - "name": "Molecule/ModeSwitch/Container", - "reusable": true, - "width": "fill_container", - "height": 48, - "layout": "horizontal", - "justifyContent": "center", - "alignItems": "center", - "gap": 4, - "padding": 4, - "fill": "$bg-surface", - "cornerRadius": "$radius-lg", + "name": "Mode Switch Showcase", + "width": 1200, + "fill": "$bg-page", + "layout": "vertical", + "gap": 40, + "padding": 40, "children": [ { "type": "frame", - "id": "mode_restaurant", - "name": "Mode/Restaurant/Active", + "id": "ModeSwitchContainer", + "name": "Molecule/ModeSwitch/Container", + "reusable": true, + "width": 600, + "height": 48, "layout": "horizontal", "justifyContent": "center", "alignItems": "center", - "gap": 8, - "padding": [8, 16], - "fill": "$vertical-restaurant", - "cornerRadius": "$radius-md", + "gap": 4, + "padding": 4, + "fill": "$bg-surface", + "cornerRadius": 12, "children": [ - { "type": "text", "content": "🍽️", "fontSize": 16 }, - { "type": "text", "content": "Nhà hàng", "fontSize": "$text-sm", "fontWeight": "$font-medium", "fill": "#FFFFFF" } + { + "type": "frame", + "id": "MSRestaurant", + "name": "modeRestaurantActive", + "layout": "horizontal", + "justifyContent": "center", + "alignItems": "center", + "gap": 8, + "padding": [8, 16], + "fill": "$orange-primary", + "cornerRadius": 8, + "children": [ + {"type": "icon_font", "id": "MSRestIcon", "iconFontName": "utensils", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "#FFFFFF"}, + {"type": "text", "id": "MSRestLabel", "content": "Nhà hàng", "fontFamily": "Roboto", "fontSize": 14, "fontWeight": "500", "fill": "#FFFFFF"} + ] + }, + { + "type": "frame", + "id": "MSBar", + "name": "modeBarInactive", + "layout": "horizontal", + "justifyContent": "center", + "alignItems": "center", + "gap": 8, + "padding": [8, 16], + "cornerRadius": 8, + "children": [ + {"type": "icon_font", "id": "MSBarIcon", "iconFontName": "wine", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "$text-secondary"}, + {"type": "text", "id": "MSBarLabel", "content": "Bar", "fontFamily": "Roboto", "fontSize": 14, "fontWeight": "500", "fill": "$text-secondary"} + ] + }, + { + "type": "frame", + "id": "MSKaraoke", + "name": "modeKaraokeInactive", + "layout": "horizontal", + "justifyContent": "center", + "alignItems": "center", + "gap": 8, + "padding": [8, 16], + "cornerRadius": 8, + "children": [ + {"type": "icon_font", "id": "MSKarIcon", "iconFontName": "mic", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "$text-secondary"}, + {"type": "text", "id": "MSKarLabel", "content": "Karaoke", "fontFamily": "Roboto", "fontSize": 14, "fontWeight": "500", "fill": "$text-secondary"} + ] + }, + { + "type": "frame", + "id": "MSSpa", + "name": "modeSpaInactive", + "layout": "horizontal", + "justifyContent": "center", + "alignItems": "center", + "gap": 8, + "padding": [8, 16], + "cornerRadius": 8, + "children": [ + {"type": "icon_font", "id": "MSSpaIcon", "iconFontName": "sparkles", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "$text-secondary"}, + {"type": "text", "id": "MSSpaLabel", "content": "Spa", "fontFamily": "Roboto", "fontSize": 14, "fontWeight": "500", "fill": "$text-secondary"} + ] + } ] }, { "type": "frame", - "id": "mode_bar", - "name": "Mode/Bar/Inactive", + "id": "ModeIndicators", + "name": "modeIndicatorsRow", "layout": "horizontal", - "justifyContent": "center", - "alignItems": "center", - "gap": 8, - "padding": [8, 16], - "fill": "transparent", - "cornerRadius": "$radius-md", + "gap": 16, "children": [ - { "type": "text", "content": "🍸", "fontSize": 16 }, - { "type": "text", "content": "Bar", "fontSize": "$text-sm", "fontWeight": "$font-medium", "fill": "$text-secondary" } - ] - }, - { - "type": "frame", - "id": "mode_karaoke", - "name": "Mode/Karaoke/Inactive", - "layout": "horizontal", - "justifyContent": "center", - "alignItems": "center", - "gap": 8, - "padding": [8, 16], - "fill": "transparent", - "cornerRadius": "$radius-md", - "children": [ - { "type": "text", "content": "🎤", "fontSize": 16 }, - { "type": "text", "content": "Karaoke", "fontSize": "$text-sm", "fontWeight": "$font-medium", "fill": "$text-secondary" } - ] - }, - { - "type": "frame", - "id": "mode_spa", - "name": "Mode/Spa/Inactive", - "layout": "horizontal", - "justifyContent": "center", - "alignItems": "center", - "gap": 8, - "padding": [8, 16], - "fill": "transparent", - "cornerRadius": "$radius-md", - "children": [ - { "type": "text", "content": "💆", "fontSize": 16 }, - { "type": "text", "content": "Spa", "fontSize": "$text-sm", "fontWeight": "$font-medium", "fill": "$text-secondary" } + { + "type": "frame", + "id": "ModeIndRestaurant", + "name": "Molecule/ModeIndicator/Restaurant", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$orange-primary", + "cornerRadius": 9999, + "children": [ + {"type": "icon_font", "id": "MIRestIcon", "iconFontName": "utensils", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "#FFFFFF"} + ] + }, + { + "type": "frame", + "id": "ModeIndBar", + "name": "Molecule/ModeIndicator/Bar", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$bar-primary", + "cornerRadius": 9999, + "children": [ + {"type": "icon_font", "id": "MIBarIcon", "iconFontName": "wine", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "#FFFFFF"} + ] + }, + { + "type": "frame", + "id": "ModeIndKaraoke", + "name": "Molecule/ModeIndicator/Karaoke", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$karaoke-primary", + "cornerRadius": 9999, + "children": [ + {"type": "icon_font", "id": "MIKarIcon", "iconFontName": "mic", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "#FFFFFF"} + ] + }, + { + "type": "frame", + "id": "ModeIndSpa", + "name": "Molecule/ModeIndicator/Spa", + "reusable": true, + "width": 32, + "height": 32, + "layout": "vertical", + "justifyContent": "center", + "alignItems": "center", + "fill": "$spa-primary", + "cornerRadius": 9999, + "children": [ + {"type": "icon_font", "id": "MISpaIcon", "iconFontName": "sparkles", "iconFontFamily": "lucide", "width": 16, "height": 16, "fill": "#FFFFFF"} + ] + } ] } ] - }, - { - "type": "frame", - "id": "mode_indicator_001", - "name": "Atom/ModeIndicator/Restaurant", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-restaurant", - "cornerRadius": "$radius-full", - "children": [ - { "type": "text", "content": "🍽️", "fontSize": 16 } - ] - }, - { - "type": "frame", - "id": "mode_indicator_002", - "name": "Atom/ModeIndicator/Bar", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-bar", - "cornerRadius": "$radius-full", - "children": [ - { "type": "text", "content": "🍸", "fontSize": 16 } - ] - }, - { - "type": "frame", - "id": "mode_indicator_003", - "name": "Atom/ModeIndicator/Karaoke", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-karaoke", - "cornerRadius": "$radius-full", - "children": [ - { "type": "text", "content": "🎤", "fontSize": 16 } - ] - }, - { - "type": "frame", - "id": "mode_indicator_004", - "name": "Atom/ModeIndicator/Spa", - "reusable": true, - "width": 32, - "height": 32, - "layout": "vertical", - "justifyContent": "center", - "alignItems": "center", - "fill": "$vertical-spa", - "cornerRadius": "$radius-full", - "children": [ - { "type": "text", "content": "💆", "fontSize": 16 } - ] } ], - "variables": {} + "variables": { + "bg-page": {"type": "color", "value": "#0A0A0B"}, + "bg-surface": {"type": "color", "value": "#111113"}, + "bg-elevated": {"type": "color", "value": "#1A1A1D"}, + "orange-primary": {"type": "color", "value": "#FF5C00"}, + "bar-primary": {"type": "color", "value": "#8B5CF6"}, + "karaoke-primary": {"type": "color", "value": "#EC4899"}, + "spa-primary": {"type": "color", "value": "#14B8A6"}, + "text-primary": {"type": "color", "value": "#FFFFFF"}, + "text-secondary": {"type": "color", "value": "#ADADB0"} + } }