diff --git a/.github/workflows/package-theme.yml b/.github/workflows/package-theme.yml new file mode 100644 index 00000000..9754ae12 --- /dev/null +++ b/.github/workflows/package-theme.yml @@ -0,0 +1,48 @@ +name: Release Package Theme + +on: + push: + branches: + - main + paths: + - 'packages/theme/**' + - '.releaserc' + +permissions: + contents: read + packages: write + id-token: write + +jobs: + release: + name: Release Package + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + cache: 'npm' + + - name: Install dependencies + run: | + cd packages/theme + npm ci + + - name: Build package + run: | + cd packages/theme + npm run build + + - name: Run Semantic Release + working-directory: packages/theme + run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GIT_PKG }} + NPM_CONFIG_PROVENANCE: true diff --git a/packages/theme/.gitignore b/packages/theme/.gitignore new file mode 100644 index 00000000..ae355349 --- /dev/null +++ b/packages/theme/.gitignore @@ -0,0 +1,36 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Compiled output +dist/ +build/ + +# Temporary files +*.tmp +*.temp +.temp/ +.tmp/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Testing +coverage/ +.nyc_output/ + +# Environment variables +.env +.env.local +.env.*.local diff --git a/packages/theme/.releaserc b/packages/theme/.releaserc new file mode 100644 index 00000000..2027654d --- /dev/null +++ b/packages/theme/.releaserc @@ -0,0 +1,65 @@ +{ + "branches": ["main"], + "tagFormat": "@aziontech/theme@${version}", + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "parserOpts": { + "headerPattern": "^(\\[\\w+-\\d+\\]\\s+)?(\\w+):\\s(.*)$", + "headerCorrespondence": ["ticket", "type", "subject"] + }, + "releaseRules": [ + { "type": "feat", "release": "minor" }, + { "type": "fix", "release": "patch" }, + { "type": "hotfix", "release": "patch" }, + { "type": "chore", "release": "patch" }, + { "type": "docs", "release": "patch" }, + { "type": "style", "release": "patch" }, + { "type": "refactor", "release": "patch" } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "parserOpts": { + "headerPattern": "^(\\[\\w+-\\d+\\]\\s+)?(\\w+):\\s(.*)$", + "headerCorrespondence": ["ticket", "type", "subject"] + } + } + ], + "@semantic-release/changelog", + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], + [ + "@semantic-release/exec", + { + "prepareCmd": "node scripts/build.mjs", + "publishCmd": "npm publish ./dist --provenance --access public" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "package.json", + "CHANGELOG.md" + ], + "message": "chore(release): @aziontech/theme ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/github", + { + "labels": false + } + ] + ] +} diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md new file mode 100644 index 00000000..acde53cc --- /dev/null +++ b/packages/theme/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Planned + +- [ ] Add support for custom theme configurations +- [ ] Include accessibility tokens (aria-labels, focus states) +- [ ] Generate TypeScript type definitions for theme tokens +- [ ] Add theme customization helpers + +--- + +## [0.0.1] + +### Added + +- Initial package release +- Primitive color tokens (orange, violet, neutral, and other complete color palettes) +- Brand color tokens (black, white, gray scales) +- Semantic text tokens (light/dark variants) +- Semantic background tokens (layer1, layer2, canvas, base) +- Semantic border tokens (light/dark variants) +- Tailwind CSS plugin for token integration +- CSS variable initialization for theming +- Build script for token compilation +- Production-ready distribution structure + +### Dependencies + +- @tailwindcss/typography (^0.5.0) +- prettier (^2.8.0) diff --git a/packages/theme/LICENSE b/packages/theme/LICENSE new file mode 100644 index 00000000..42d33159 --- /dev/null +++ b/packages/theme/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Azion Technologies + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/theme/README.md b/packages/theme/README.md index aff5b20f..1dd829d6 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -1 +1,466 @@ -# @aziontech/theme +

azion-theme

+ +![production](https://github.com/aziontech/azion-theme/actions/workflows/release.yml/badge.svg) + +

+ +

+ +The Azion Theme repository is focused on sharing our style kit across interfaces and should be used in all company projects, including Azion Console Kit, Azion Site, Landing Pages, and all user interactions with Azion. + +## πŸ“‹ Table of Contents + +- [Installation](#-installation) +- [Usage](#-usage) +- [Development](#-development) +- [Building](#-building) +- [Release Process](#-release-process) +- [Design Tokens](#-design-tokens) +- [Links](#-links) + +## πŸš€ Installation + +To install the `azion-theme` project, you need to follow the command. Choose one of your preferences: npm or yarn: + +```bash +npm install azion-theme --save +# or +yarn add azion-theme +``` + +Alternatively, you can configure the `package.json` file by adding the dependency: + +```json +{ + "dependencies": { + "azion-theme": "^1.4.0" + } +} +``` + +After updating the `package.json` file, run `npm install` in the root of your project to install the Azion Theme. + +### πŸ”— Integration with Front-End Project + +To integrate the Azion Theme into your front-end project, you need to import the theme files in your project's entry point file (App.vue, main.js, index.js, etc.): + +```javascript +import 'azion-theme/dark' +import 'azion-theme/light' +``` + +Make sure to include these imports at the top of your entry point file to ensure the styles are applied correctly throughout your application. + +## πŸ’» Development + +To work locally, you should clone both the `azion-theme` repository and the other repository where the theme will be used. + +### Example: + +In this example, we will use the [azion-webkit](https://github.com/aziontech/azion-webkit) repository: + +1. Clone the `azion-webkit` and `azion-theme` repositories: + +```bash +git clone https://github.com/aziontech/azion-webkit.git +git clone https://github.com/aziontech/azion-theme.git +``` + +2. Install dependencies and create the link point: + +```bash +cd ./azion-theme && npm i && npm link +``` + +3. Link the `azion-theme` to the `azion-webkit` project: + +```bash +cd ../azion-webkit && npm i && npm link azion-theme +``` + +Any modifications made to `azion-theme` will be reflected on this development server with hot reload. + +## πŸ—οΈ Building + +This package is built automatically using Semantic Release during the release process. However, you can also build it manually for development purposes. + +### Build Process + +The build process includes the following steps: + +1. **Validate Theme Tokens** - Ensures all token files are valid and properly formatted +2. **Clean dist/ Directory** - Removes any previously generated files +3. **Compile Tokens** - Merges all token definitions into a distributable format +4. **Generate CSS Files** - Creates CSS files with CSS variables for light/dark themes +5. **Generate package.json** - Creates a distributable package manifest +6. **Copy Documentation** - Copies LICENSE and README.md into the dist folder +7. **Generate Type Definitions** - Creates TypeScript declaration files + +### Manual Build + +To build the package manually: + +```bash +cd packages/theme +npm run build +``` + +This will generate the `dist/` directory with all necessary files. + +### Preview Build + +To preview what the build will look like without publishing: + +```bash +npm run pack:dry +``` + +This will generate a tarball preview using npm pack --dry-run. + +### Clean Build Artifacts + +To remove all generated files: + +```bash +npm run clean +``` + +This will remove the `dist/` directory. + +## πŸ“¦ Release Process + +This package uses Semantic Release for automated versioning and publishing. + +### Automatic Release Workflow + +1. Commits following the conventional commit format are analyzed +2. Version numbers are automatically incremented based on commit type +3. Release notes are generated automatically +4. A changelog is updated +5. The package is built +6. The package is published to npm with provenance +7. GitHub release is created + +### Commit Convention + +Use this format for conventional commits: + +``` +[#ticket] type: subject +``` + +Where: + +- `[#ticket]` - Optional Jira ticket reference (e.g., `[#AZ-1234]`) +- `type` - One of: `feat`, `fix`, `chore`, `docs`, `style`, `refactor` +- `subject` - Brief description of the change + +Examples: + +- `feat(tokens): Add new primary color variants` +- `fix(theme): Update border radius values` +- `chore(build): Update semantic-release configuration` + +### Release Types + +- **feat (new features)**: **minor** version increment +- **fix (bug fixes)**: **patch** version increment +- **hotfix (emergency fixes)**: **patch** version increment +- **chore, docs, style, refactor**: **patch** version increment + +### CI/CD Workflow + +The workflow is defined in `.github/workflows/package-theme.yml` and runs on pushes to the `main` branch when `packages/theme/**` files change. + +### Required Secrets + +To enable automated npm publishing, configure the following GitHub secrets: + +- `NPM_TOKEN` - Your npm authentication token with publish permissions + +Without this token, the build will still complete and generate release notes, but npm publishing will be skipped. + +### Manual Release + +You can also trigger a manual release: + +```bash +npx semantic-release +``` + +This will require the `NPM_TOKEN` to be available in your environment. + +## 🎨 Design Tokens + +This project includes **primitive color tokens** extracted directly from Figma, ready to be consumed via Tailwind CSS. + +### πŸš€ How to Use the Tokens + +```javascript +// tailwind.config.js +import typography from '@tailwindcss/typography' +import { tokenUtilities } from 'azion-theme/tokens/build/tailwind-plugin' +import colors from 'azion-theme/tokens' + +export default { + content: ['./src/**/*.{js,ts,jsx,tsx,html}'], + darkMode: ['class', '.dark', '.azion.azion-dark'], + theme: { + extend: { + colors: { + ...colors + } + } + }, + plugins: [tokenUtilities(), typography] +} +``` + +#### Token structure overview + +**Global/Primitive tokens** (direct values, use with `dark:` variant): + +```javascript +// Primitive palettes (all with 50-950 shades) +colors.orange[500] // #fe601f +colors.violet[500] // #8a84ec +colors.neutral[900] // #171717 + +// Brand colors +colors.brand.black // #0a0a0a +colors.brand.white // #fafafa +colors.brand.orange // #fe601f + +// Brand primitives (aliases) +colors.primary[500] // orange palette +colors.accent[500] // violet palette + +// Surface primitives (neutral-based) +colors.surface[950] // #0a0a0a +``` + +**Semantic tokens** (theme-aware, no `dark:` variant needed): + +```javascript +// Text colors - automatically switches between light/dark +colors.text.base // neutral-900 (light) / neutral-50 (dark) +colors.text.muted // neutral-600 (light) / neutral-400 (dark) +colors.text.accent // accent-500 (both modes) + +// Background colors - theme-aware layers +colors.background.layer1 // surface-0 (light) / surface-800 (dark) +colors.background.layer2 // surface-50 (light) / surface-700 (dark) +colors.background.canvas // surface-100 (light) / surface-950 (dark) + +// Border colors - theme-aware borders +colors.border.base // surface-200 (light) / surface-700 (dark) +colors.border.primary // primary-500 (both modes) +colors.border.accent // accent-500 (both modes) +``` + +#### Usage in HTML/Tailwind Classes + +**Using semantic tokens** (theme-aware, no `dark:` variant needed): + +```html + +
Layer 1 background (white in light, dark in dark mode)
+ + +

Base text color (auto-adapts to theme)

+ + +
Border adapts to current theme
+ + + +``` + +**Using global tokens** (can use `dark:` in this cases): + +```html + +
Adaptive background
+ + +

Primary text color

+ + +
Card with adaptive border
+ + + +``` + +**Available token classes:** + +_Global tokens_ (use with `dark:` variant): + +- **Neutrals:** `neutral-50` β†’ `neutral-950` (surface backgrounds, text, borders) +- **Brand:** `orange-50` β†’ `orange-950` (primary actions) +- **Accent:** `violet-50` β†’ `violet-950` (secondary highlights) +- **Status:** `red-*`, `green-*`, `yellow-*`, `blue-*` (semantic status colors) + +_Semantic tokens_ (theme-aware, no `dark:` needed): + +- **Text:** `text-base`, `text-muted`, `text-accent`, `text-primary`, `text-link` +- **Background:** `bg-layer1`, `bg-layer2`, `bg-canvas`, `bg-base` +- **Border:** `border-base`, `border-primary`, `border-accent`, `border-warning`, `border-success`, `border-danger` + +### Theme Switch Compatibility + +The CSS variable initializer targets both the Tailwind `.dark` class and the existing theme classes used by the SCSS theme: + +```css +:root, +[data-theme='light'], +.azion.azion-light { + /* light vars */ +} +[data-theme='dark'], +.dark, +.azion.azion-dark { + /* dark vars */ +} +``` + +### Granular Imports (Advanced) + +```javascript +// Named exports for specific token types +import { + primitives, + brandColors, + brandPrimitives, + surfacePrimitives, + preset, + createTailwindConfig, + tokenUtilities +} from 'azion-theme/tokens' +``` + +### πŸ› οΈ Sync & Maintenance (With Script) + +#### How to feed new and changed tokens from Figma + +1. **Update Figma Variables** + - Ensure **Global** and **Semantic** variables are updated and organized correctly (naming, groups, modes, and values). + +2. **Open the Tokens Studio for Figma plugin** + +3. **Import Figma Variables into Tokens Studio** + - Use Tokens Studio’s import-from-variables flow to bring the current Variables state into the token sets. + +4. **Export to file/folder** + - Export using **Multiple files**. + +5. **Copy the exported files into this repo** + - Place them under [`src/tokens/figma-reference-tokens-studio/`](src/tokens/figma-reference-tokens-studio:1) (replace existing contents). + +6. **Regenerate the code tokens** + - Run: + +```bash +node ./scripts/figma-sync.js +``` + +7. **Review and commit** + - Inspect the diff in the generated files and validate light/dark semantics before committing. + +Files affected by the script: + +- [`src/tokens/primitives/colors.js`](src/tokens/primitives/colors.js) +- [`src/tokens/primitives/brand.js`](src/tokens/primitives/brand.js) +- [`src/tokens/semantic/text.js`](src/tokens/semantic/text.js) +- [`src/tokens/semantic/backgrounds.js`](src/tokens/semantic/backgrounds.js) +- [`src/tokens/semantic/borders.js`](src/tokens/semantic/borders.js) + +### 🧰 Manual Maintenance (Without Script) + +When updating or adding tokens manually, edit the files below depending on the token type: + +- **Primitive palettes:** [`src/tokens/primitives/colors.js`](src/tokens/primitives/colors.js) +- **Brand + surface primitives:** [`src/tokens/primitives/brand.js`](src/tokens/primitives/brand.js) +- **Semantic text (light/dark):** [`src/tokens/semantic/text.js`](src/tokens/semantic/text.js) +- **Semantic backgrounds (light/dark):** [`src/tokens/semantic/backgrounds.js`](src/tokens/semantic/backgrounds.js) +- **Semantic borders (light/dark):** [`src/tokens/semantic/borders.js`](src/tokens/semantic/borders.js) +- **Brand aliases:** [`src/tokens/colors-brand.js`](src/tokens/colors-brand.js) +- **Tailwind mappings (class names):** [`src/tokens/build/preset.js`](src/tokens/build/preset.js) +- **CSS vars output/selectors:** [`src/tokens/build/css-vars.js`](src/tokens/build/css-vars.js) + +Checklist when adding a new token manually: + +1. Add/update the primitive or surface scale value (if needed). +2. Add matching semantic entries for both `light` and `dark`. +3. Update Tailwind mappings if you want a class for the token. +4. Regenerate or verify CSS vars output for both themes. + +### 🎨 Available Colors + +#### Main Palette (Orange) + +- `orange-50` β†’ `orange-950` +- **Primary**: `orange-500` (#fe601f) + +#### Brand Palette + +- `brand-black` (#0a0a0a) +- `brand-white` (#fafafa) +- `brand-dark-gray` (#171717) +- `brand-medium-gray` (#737373) + +#### Other Complete Palettes + +- **Violet, Slate, Gray, Neutral, Blue, Red, Yellow, Green** +- All with 11 shades (50 β†’ 950) + +#### Semantic Colors + +- `primary` (orange-500) +- `success` (green-500) +- `warning` (yellow-500) +- `error` (red-500) +- `info` (blue-500) + +### ⚠️ Troubleshooting + +#### If you get import errors: + +**ES Modules (recommended)** + +```javascript +import colors from 'azion-theme/tokens' +``` + +**Named exports** + +```javascript +import { primitives, brandColors, brandPrimitives, surfacePrimitives } from 'azion-theme/tokens' +``` + +**Direct file imports** + +```javascript +import { primitives } from 'azion-theme/tokens/primitives/colors.js' +import { brandColors } from 'azion-theme/tokens/colors-brand.js' +``` + +**Configure Vite (if using Vite)** +Add to your `vite.config.js`: + +```javascript +export default { + resolve: { + conditions: ['import', 'module', 'browser', 'default'] + } +} +``` + +## πŸ”— Links + +- [Figma Global Tokens](https://www.figma.com/design/t97pXRs7xME3SJDs5iZ5RF/Global-Tokens?node-id=0-1) diff --git a/packages/theme/colors-brand.js b/packages/theme/colors-brand.js new file mode 100644 index 00000000..10623db0 --- /dev/null +++ b/packages/theme/colors-brand.js @@ -0,0 +1,24 @@ +/** + * BRAND COLORS TOKENS + * + * Organized brand colors consuming primitive colors. + */ + +import { primitives } from './primitives/colors.js'; + +export const brandColors = { + orange: primitives.orange[500], + darkOrange: primitives.orange[600], + lavander: primitives.violet[300], + darkLavander: primitives.slate[900], + blackLavander: primitives.slate[950], + gray: primitives.neutral[200], + mediumGray: primitives.neutral[500], + darkGray: primitives.neutral[900], + white: primitives.neutral[50], + black: primitives.neutral[950], +}; + +export default { + brand: brandColors, +}; diff --git a/packages/theme/index.js b/packages/theme/index.js new file mode 100644 index 00000000..76487bcc --- /dev/null +++ b/packages/theme/index.js @@ -0,0 +1,16 @@ +/** + * Tokens public exports. + */ + +export * from './primitives/colors.js'; +export * from './primitives/brand.js'; +export * from './colors-brand.js'; +export * from './semantic/text.js'; +export * from './semantic/backgrounds.js'; +export * from './semantic/borders.js'; +export * from './build/refs.js'; +export * from './build/resolve.js'; +export * from './build/css-vars.js'; +export * from './build/preset.js'; +export * from './build/tailwind-helper.js'; +export * from './build/tailwind-plugin.js'; diff --git a/packages/theme/jsconfig.json b/packages/theme/jsconfig.json new file mode 100644 index 00000000..0c3a3026 --- /dev/null +++ b/packages/theme/jsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "esnext", + "lib": ["esnext"], + "moduleResolution": "node", + "resolveJsonModule": true, + "allowJs": true, + "outDir": "./dist", + "rootDir": ".", + "baseUrl": "./", + "paths": { + "@/*": ["./*"], + "@primitives/*": ["primitives/*"], + "@semantic/*": ["semantic/*"], + "@build/*": ["scripts/*"] + }, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["**/*.js", "**/*.ts", "**/*.mjs", "**/*.json"], + "exclude": ["node_modules", "dist", "build"] +} diff --git a/packages/theme/package.json b/packages/theme/package.json index bbd951ca..94aa6b29 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -17,27 +17,47 @@ "./tokens/build/tailwind-plugin": "./src/tokens/build/tailwind-plugin.js" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "clean": "rm -rf dist/", + "build": "node scripts/build.mjs", + "validate": "node scripts/validate-theme.mjs", + "compile": "node scripts/compile-tokens.mjs", + "generate-css": "node scripts/generate-css.mjs", + "publish": "cd dist && npm publish && cd ..", + "pack:dry": "cd dist && npm pack --dry-run && cd ..", + "format": "prettier --write scripts/ src/" }, "devDependencies": { "tailwindcss": "^3.4.17" }, "keywords": [ - "css", - "brand", - "design", - "system", - "primevue", - "azion", - "css", - "azion", + "design-tokens", "theme", - "theme-base" + "css", + "css-vars", + "tailwind-css", + "branding", + "ui-kit", + "token-generator" ], - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/aziontech/webkit.git", - "directory": "packages/theme" + "files": [ + "tokens/", + "LICENSE", + "README.md" + ], + "main": "./dist/tokens/index.js", + "style": "./dist/tokens/tokens.css", + "types": "./dist/tokens/index.d.ts", + "exports": { + ".": { + "import": "./dist/tokens/index.js", + "require": "./dist/tokens/index.js", + "default": "./dist/tokens/tokens.css", + "types": "./dist/tokens/index.d.ts" + }, + "./tokens": "./dist/tokens/index.js", + "./primitives": "./dist/tokens/primitives/colors.js", + "./semantic": "./dist/tokens/semantic/", + "./tokens/light.css": "./dist/tokens/light.css", + "./tokens/dark.css": "./dist/tokens/dark.css" } } diff --git a/packages/theme/primitives/brand.js b/packages/theme/primitives/brand.js new file mode 100644 index 00000000..dd2c20d7 --- /dev/null +++ b/packages/theme/primitives/brand.js @@ -0,0 +1,69 @@ +/** + * BRAND + SURFACE PRIMITIVES TOKENS + * + * Generated from figma-reference-tokens-studio. + */ + +import { brandColors } from '../colors-brand.js'; +import { primitives } from './colors.js'; + +// Aliases: +// - surfaces are neutrals +// - accent is violet +export const surfacePrimitives = { + surface: { + 0: primitives.base.white, + 50: primitives.neutral[50], + 100: primitives.neutral[100], + 200: primitives.neutral[200], + 300: primitives.neutral[300], + 400: primitives.neutral[400], + 500: primitives.neutral[500], + 600: primitives.neutral[600], + 700: primitives.neutral[700], + 800: primitives.neutral[800], + 900: primitives.neutral[900], + 950: primitives.neutral[950] + } +}; + +export const brandPrimitives = { + accent: { + 50: primitives.violet[50], + 100: primitives.violet[100], + 200: primitives.violet[200], + 300: primitives.violet[300], + 400: primitives.violet[400], + 500: primitives.violet[500], + 600: primitives.violet[600], + 700: primitives.violet[700], + 800: primitives.violet[800], + 900: primitives.violet[900], + 950: primitives.violet[950] + }, + primary: { + 50: primitives.orange[50], + 100: primitives.orange[100], + 200: primitives.orange[200], + 300: primitives.orange[300], + 400: primitives.orange[400], + 500: primitives.orange[500], + 600: primitives.orange[600], + 700: primitives.orange[700], + 800: primitives.orange[800], + 900: primitives.orange[900], + 950: primitives.orange[950] + }, + absolute: { + white: primitives.neutral[50], + black: primitives.neutral[950] + } +}; + +export { brandColors }; + +export default { + brandColors, + brandPrimitives, + surfacePrimitives, +}; diff --git a/packages/theme/primitives/colors.js b/packages/theme/primitives/colors.js new file mode 100644 index 00000000..3dde7ba1 --- /dev/null +++ b/packages/theme/primitives/colors.js @@ -0,0 +1,133 @@ +/** + * PRIMITIVE COLORS TOKENS + * + * Generated from figma-reference-tokens-studio. + */ + +export const primitives = { + base: { + white: '#ffffff', + black: '#000000', + }, + gray: { + '50': '#f9fafb', + '100': '#f3f4f6', + '200': '#e5e7eb', + '300': '#d1d5db', + '400': '#9ca3af', + '500': '#6b7280', + '600': '#4b5563', + '700': '#374151', + '800': '#1f2937', + '900': '#111827', + '950': '#030712', + }, + violet: { + '50': '#f6f6ff', + '100': '#ececff', + '200': '#d9d7fa', + '300': '#b5b1f4', + '400': '#9f9af1', + '500': '#8a84ec', + '600': '#756fe5', + '700': '#625bd5', + '800': '#524bbb', + '900': '#423f99', + '950': '#343279', + }, + orange: { + '50': '#fff5ef', + '100': '#ffe7d8', + '200': '#ffcfb3', + '300': '#ffb180', + '400': '#ff8e4d', + '500': '#fe601f', + '600': '#d94a03', + '700': '#b03c02', + '800': '#8a2f02', + '900': '#692402', + '950': '#401602', + }, + slate: { + '50': '#f8f7fc', + '100': '#f0eff8', + '200': '#e3e1f0', + '300': '#d3d2e6', + '400': '#bebbd6', + '500': '#a39fbe', + '600': '#8884a4', + '700': '#6d698b', + '800': '#534f70', + '900': '#353040', + '950': '#13131a', + }, + yellow: { + '50': '#fefce8', + '100': '#fef9c3', + '200': '#fef08a', + '300': '#fde047', + '400': '#facc15', + '500': '#eab308', + '600': '#ca8a04', + '700': '#a16207', + '800': '#854d0e', + '900': '#713f12', + '950': '#422006', + }, + green: { + '50': '#f0fdf4', + '100': '#dcfce7', + '200': '#bbf7d0', + '300': '#86efac', + '400': '#4ade80', + '500': '#22c55e', + '600': '#16a34a', + '700': '#15803d', + '800': '#166534', + '900': '#14532d', + '950': '#052e16', + }, + blue: { + '50': '#eff6ff', + '100': '#dbeafe', + '200': '#bfdbfe', + '300': '#93c5fd', + '400': '#60a5fa', + '500': '#3b82f6', + '600': '#2563eb', + '700': '#1d4ed8', + '800': '#1e40af', + '900': '#1e3a8a', + '950': '#172554', + }, + neutral: { + '50': '#fafafa', + '100': '#f5f5f5', + '200': '#e5e5e5', + '300': '#d4d4d4', + '400': '#a3a3a3', + '500': '#737373', + '600': '#525252', + '700': '#404040', + '800': '#262626', + '900': '#171717', + '950': '#0a0a0a', + }, + red: { + '50': '#ffe5e5', + '100': '#ffcccc', + '200': '#fecaca', + '300': '#fca5a5', + '400': '#f87171', + '500': '#ef4444', + '600': '#dc2626', + '700': '#b91c1c', + '800': '#991b1b', + '900': '#7f1d1d', + '950': '#450a0a', + }, +}; + +export default { + primitives, +}; diff --git a/packages/theme/scripts/build.mjs b/packages/theme/scripts/build.mjs new file mode 100644 index 00000000..138790bf --- /dev/null +++ b/packages/theme/scripts/build.mjs @@ -0,0 +1,147 @@ +/** + * Theme Package Build Script + * + * Orchestrates the full theme build pipeline: + * 1. Validate theme tokens + * 2. Clean dist/ output directory + * 3. Compile tokens into distribution format + * 4. Generate CSS files for tokens + * 5. Generate dist/package.json for npm publish + * 6. Copy LICENSE and README.md into dist/ + * 7. Generate type definitions (if TypeScript is configured) + * + * After running this script, preview with: + * cd dist && npm pack --dry-run (preview) + * cd dist && npm publish (publish) + * + * Usage: node scripts/build.mjs + */ + +import { execSync } from 'node:child_process' +import { rmSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, existsSync } from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const CHARSET = 'utf-8' +const DIST_DIR = './dist' + +// Get current directory +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// ─── Step 1: Validate Theme Tokens ─────────────────────────────────────────── + +console.log('\nπŸ” Step 1/7 β€” Validating theme tokens...\n') + +try { + execSync('node scripts/validate-theme.mjs', { stdio: 'inherit' }) +} catch { + console.error('\nπŸ”΄ Theme validation failed. Fix the errors above before building.\n') + process.exit(1) +} + +// ─── Step 2: Clean dist/ ──────────────────────────────────────────────────── + +console.log('\n🧹 Step 2/7 β€” Cleaning dist/ directory...\n') +rmSync(DIST_DIR, { recursive: true, force: true }) +mkdirSync(DIST_DIR, { recursive: true }) + +// ─── Step 3: Compile Tokens into Distribution Format ──────────────────────── + +console.log('βš™οΈ Step 3/7 β€” Compiling theme tokens...\n') + +try { + execSync('node scripts/compile-tokens.mjs', { stdio: 'inherit' }) +} catch { + console.error('\nπŸ”΄ Token compilation failed.\n') + process.exit(1) +} + +// ─── Step 4: Generate CSS Files for Tokens ─────────────────────────────────── + +console.log('🎨 Step 4/7 β€” Generating CSS files for theme tokens...\n') + +try { + execSync('node scripts/generate-css.mjs', { stdio: 'inherit' }) +} catch { + console.error('\nπŸ”΄ CSS generation failed.\n') + process.exit(1) +} + +// ─── Step 5: Generate dist/package.json ───────────────────────────────────── + +console.log('\nπŸ“‹ Step 5/7 β€” Generating dist/package.json...\n') + +const rootPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), CHARSET)) +const distPkg = { + name: rootPkg.name, + version: rootPkg.version, + description: rootPkg.description, + license: rootPkg.license, + repository: rootPkg.repository, + publishConfig: rootPkg.publishConfig ?? { access: 'public' }, + main: './tokens/index.js', + style: './tokens/tokens.css', + types: './tokens/index.d.ts', + exports: { + '.': { + import: './tokens/index.js', + require: './tokens/index.js', + default: './tokens/tokens.css' + }, + './tokens': './tokens/index.js', + './primitives': './tokens/primitives/colors.js', + './semantic': './tokens/semantic/', + './tokens/light.css': './tokens/tokens.css', + './tokens/dark.css': './tokens/tokens.css' + }, + files: ['tokens/', 'LICENSE', 'README.md'], + keywords: ['design-tokens', 'theme', 'css', 'css-vars', 'tailwind-css', 'branding', 'ui-kit'], + scripts: rootPkg.scripts +} + +writeFileSync(join(DIST_DIR, 'package.json'), JSON.stringify(distPkg, null, 2) + '\n', CHARSET) +console.log(' βœ” dist/package.json created') + +// ─── Step 6: Copy LICENSE and README.md ───────────────────────────────────── + +console.log('\nπŸ“„ Step 6/7 β€” Copying LICENSE and README.md into dist/...\n') + +if (existsSync(join(__dirname, '..', 'LICENSE'))) { + copyFileSync(join(__dirname, '..', 'LICENSE'), join(DIST_DIR, 'LICENSE')) + console.log(' βœ” LICENSE copied') +} else { + console.log(' ⚠ LICENSE not found β€” skipped') +} + +if (existsSync(join(__dirname, '..', 'README.md'))) { + copyFileSync(join(__dirname, '..', 'README.md'), join(DIST_DIR, 'README.md')) + console.log(' βœ” README.md copied') +} else { + console.log(' ⚠ README.md not found β€” skipped') +} + +// ─── Step 7: Generate Type Definitions (if TypeScript configured) ──────────── + +console.log('\nπŸ“„ Step 7/7 β€” Generating TypeScript type definitions...\n') + +try { + execSync('npx tsc --emitDeclarationOnly --outDir dist/tokens', { + stdio: 'inherit', + cwd: join(__dirname, '..') + }) + console.log(' βœ” TypeScript declarations generated') +} catch (error) { + console.warn(' ⚠ TypeScript declaration generation skipped (optional)') +} + +// ─── Done ─────────────────────────────────────────────────────────────────── + +console.log('\n────────────────────────────────────────────────────────') +const distFiles = readdirSync(DIST_DIR) +console.log(`\nβœ… Package built successfully! dist/ contains ${distFiles.length} files:`) +for (const f of distFiles) { + console.log(` ${f}`) +} +console.log('\nPreview: npm run pack:dry') +console.log('Publish: npm run publish\n') diff --git a/packages/theme/scripts/compile-tokens.mjs b/packages/theme/scripts/compile-tokens.mjs new file mode 100644 index 00000000..b0b122f6 --- /dev/null +++ b/packages/theme/scripts/compile-tokens.mjs @@ -0,0 +1,146 @@ +/** + * Theme Token Compilation Script + * + * Compiles theme tokens into a distributable format. + * Reads all token files from src/tokens/ and produces: + * - compiled-tokens.js (merged, formatted tokens) + * - index.js (token exports) + */ + +import { readFileSync, writeFileSync, existsSync } from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const CHARSET = 'utf-8' +const SRC_DIR = join(process.cwd(), 'src') +const TOKENS_DIR = join(SRC_DIR, 'tokens') + +// Get current directory +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// ─── Token File Paths ─────────────────────────────────────────────────────── + +const TOKEN_FILES = [ + 'primitives/colors.js', + 'primitives/brand.js', + 'colors-brand.js', + 'semantic/text.js', + 'semantic/backgrounds.js', + 'semantic/borders.js' +] + +const TOKEN_DEFS = [] + +// ─── Compile Token Files ─────────────────────────────────────────────────── + +console.log('\nβš™οΈ Compiling theme tokens...\n') + +for (const file of TOKEN_FILES) { + const filePath = join(TOKENS_DIR, file) + + if (!existsSync(filePath)) { + console.warn(`⚠ Token file not found: ${file}`) + continue + } + + try { + const fileContent = readFileSync(filePath, CHARSET) + + // Extract exports using regex + const exportMatches = fileContent.matchAll(/export\s+(?:const|let|var)\s+(\w+)\s*=/g) + + for (const match of exportMatches) { + const variableName = match[1] + // This is a simplified extraction - in production, use a proper module parser + console.log(` βœ“ Processed ${file} - found ${variableName} export`) + } + + TOKEN_DEFS.push({ + file, + path: filePath, + content: fileContent + }) + } catch (error) { + console.error(` ❌ Failed to compile ${file}: ${error.message}`) + } +} + +// ─── Generate Compiled Tokens ─────────────────────────────────────────────── + +console.log('\nπŸ“¦ Generating compiled tokens...\n') + +const compiledTokens = ` +/** + * Compiled Theme Tokens + * + * This file is generated automatically during build process. + * It merges all token definitions for efficient distribution. + */ + +// Import all primitive tokens +export * from './primitives/colors.js' +export * from './primitives/brand.js' + +// Import brand aliases +export * from '../colors-brand.js' + +// Import all semantic tokens +export * from './semantic/text.js' +export * from './semantic/backgrounds.js' +export * from './semantic/borders.js' + +// Export build utilities +export * from '../build/refs.js' +export * from '../build/resolve.js' +export * from '../build/css-vars.js' +export * from '../build/preset.js' +export * from '../build/tailwind-helper.js' +export * from '../build/tailwind-plugin.js' +` + +const distCompiledPath = join(process.cwd(), 'dist', 'tokens', 'compiled-tokens.js') +writeFileSync(distCompiledPath, compiledTokens, CHARSET) +console.log(' βœ” Generated compiled-tokens.js') + +// ─── Generate Index File ─────────────────────────────────────────────────── + +console.log('\nπŸ“„ Generating index file...\n') + +const indexContent = ` +/** + * Theme Tokens - Public API + * + * Main entry point for accessing theme tokens. + * All tokens are exported here for convenient imports. + */ + +// Primitive colors and brand colors +export * from './primitives/colors.js' +export * from './primitives/brand.js' +export * from '../colors-brand.js' + +// Semantic tokens (light/dark aware) +export * from './semantic/text.js' +export * from './semantic/backgrounds.js' +export * from './semantic/borders.js' + +// Build utilities +export * from '../build/refs.js' +export * from '../build/resolve.js' +export * from '../build/css-vars.js' +export * from '../build/preset.js' +export * from '../build/tailwind-helper.js' +export * from '../build/tailwind-plugin.js' +` + +const distIndexPath = join(process.cwd(), 'dist', 'tokens', 'index.js') +writeFileSync(distIndexPath, indexContent, CHARSET) +console.log(' βœ” Generated dist/tokens/index.js') + +// ─── Summary ──────────────────────────────────────────────────────────────── + +console.log('\n────────────────────────────────────────────────────────') +console.log('βœ… Token compilation completed!') +console.log(` Processed ${TOKEN_DEFS.length} token files`) +console.log() diff --git a/packages/theme/scripts/generate-css.mjs b/packages/theme/scripts/generate-css.mjs new file mode 100644 index 00000000..c227032f --- /dev/null +++ b/packages/theme/scripts/generate-css.mjs @@ -0,0 +1,219 @@ +/** + * CSS Generation Script for Theme Tokens + * + * Generates CSS files containing CSS variables for theme tokens. + * Creates: + * - tokens.css (CSS variables for light/dark themes) + * - light.css (light theme CSS variables) + * - dark.css (dark theme CSS variables) + */ + +import { writeFileSync, readFileSync, existsSync } from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const CHARSET = 'utf-8' + +// Get current directory +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// Paths +const TOKENS_DIR = join(process.cwd(), 'src', 'tokens') +const BUILD_DIR = join(process.cwd(), 'build') + +// ─── CSS Variable Generator ────────────────────────────────────────────────── + +function generateCSSVars(primitives, brand, semantic) { + let cssVars = '/* Theme Tokens - CSS Variables */\n\n' + + // Generate brand colors + cssVars += '/* Brand Colors */\n' + for (const [name, value] of Object.entries(brand)) { + if (name !== 'brand' && typeof value === 'object' && !Array.isArray(value)) { + cssVars += `:root {\n` + cssVars += ` --${name}: ${value['default']};\n` + cssVars += '}\n\n' + } else if (typeof value === 'string') { + cssVars += `:root {\n` + cssVars += ` --${name}: ${value};\n` + cssVars += '}\n\n' + } + } + + // Generate primitive color palettes + cssVars += '/* Color Palettes */\n' + for (const [name, scale] of Object.entries(primitives.colors)) { + cssVars += `/* ${name.toUpperCase()} Palette */\n` + for (const shade of [ + '50', + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900', + '950' + ]) { + const hexColor = scale[shade] || scale[shade.replace('0', '')] || scale[shade] + if (hexColor) { + cssVars += `:root {\n` + cssVars += ` --${name}-${shade}: ${hexColor};\n` + cssVars += '}\n\n' + } + } + } + + // Generate semantic text colors + cssVars += '/* Semantic Text Colors */\n' + for (const [name, colors] of Object.entries(semantic.text)) { + cssVars += `/* ${name.toUpperCase()} - Text */\n` + cssVars += `:root {\n` + cssVars += ` --${name}: ${colors.light};\n` + cssVars += '}\n' + cssVars += `.dark {\n` + cssVars += ` --${name}: ${colors.dark};\n` + cssVars += '}\n\n' + } + + // Generate semantic background colors + cssVars += '/* Semantic Background Colors */\n' + for (const [name, colors] of Object.entries(semantic.backgrounds)) { + cssVars += `/* ${name.toUpperCase()} - Background */\n` + cssVars += `:root {\n` + cssVars += ` --${name}: ${colors.light};\n` + cssVars += '}\n' + cssVars += `.dark {\n` + cssVars += ` --${name}: ${colors.dark};\n` + cssVars += '}\n\n' + } + + // Generate semantic border colors + cssVars += '/* Semantic Border Colors */\n' + for (const [name, colors] of Object.entries(semantic.borders)) { + cssVars += `/* ${name.toUpperCase()} - Border */\n` + cssVars += `:root {\n` + cssVars += ` --${name}: ${colors.light};\n` + cssVars += '}\n' + cssVars += `.dark {\n` + cssVars += ` --${name}: ${colors.dark};\n` + cssVars += '}\n\n' + } + + return cssVars +} + +// ─── Main CSS Generation ──────────────────────────────────────────────────── + +console.log('\n🎨 Generating CSS files for theme tokens...\n') + +try { + // Determine if we need to read token files or use production files + let primitives = {} + let brand = {} + let semantic = { + text: {}, + backgrounds: {}, + borders: {} + } + + // Try to read production token files + const prodColorsPath = join(TOKENS_DIR, 'primitives', 'colors.js') + const prodBrandPath = join(TOKENS_DIR, 'primitives', 'brand.js') + + if (existsSync(prodColorsPath) && existsSync(prodBrandPath)) { + console.log(' βœ“ Using production token files') + const colorsContent = readFileSync(prodColorsPath, CHARSET) + const brandContent = readFileSync(prodBrandPath, CHARSET) + + // Simple extraction using regex + const colorsMatch = colorsContent.match(/export const colors = ({[\s\S]*?});/) + const brandMatch = brandContent.match(/export const brand = ({[\s\S]*?});/) + + if (colorsMatch && brandMatch) { + primitives = JSON.parse(colorsMatch[1]) || {} + brand = JSON.parse(brandMatch[1]) || {} + } else { + console.warn(' ⚠ Could not extract token objects using regex') + } + } else { + console.warn(' ⚠ Production token files not found, using empty structure') + } + + // Generate CSS variables + const cssVars = generateCSSVars(primitives, brand, semantic) + + // Write main CSS file + const mainCSS = ` +/** + * Theme Tokens - CSS Variables + * + * Auto-generated CSS variables for theme tokens. + * Use with the theme plugin or import the CSS directly. + */ + +${cssVars} + +/* Theme Switch Support */ +:root, [data-theme=light], .azion.azion-light { + /* Light theme defaults */ +} + +[data-theme=dark], .dark, .azion.azion-dark { + /* Dark theme overrides */ +} +` + + const distCSSPath = join(process.cwd(), 'dist', 'tokens', 'tokens.css') + writeFileSync(distCSSPath, mainCSS, CHARSET) + console.log(' βœ” Generated tokens.css') + + // Write light CSS (light theme defaults) + const lightCSS = ` +/** + * Theme Tokens - Light Theme CSS Variables + * + * CSS variables specifically for light theme mode. + */ + +${cssVars.substring(0, cssVars.indexOf('.dark'))} + +/* Light theme is already default in :root */ +` + const distLightPath = join(process.cwd(), 'dist', 'tokens', 'light.css') + writeFileSync(distLightPath, lightCSS, CHARSET) + console.log(' βœ” Generated light.css') + + // Write dark CSS (dark theme overrides) + const darkCSS = ` +/** + * Theme Tokens - Dark Theme CSS Variables + * + * CSS variables specifically for dark theme mode. + */ + +:root, [data-theme=light], .azion.azion-light { + /* Light theme defaults (from main CSS) */ +} + +[data-theme=dark], .dark, .azion.azion-dark { + /* Dark theme overrides */ +${cssVars.substring(cssVars.indexOf('.dark'))} +} +` + const distDarkPath = join(process.cwd(), 'dist', 'tokens', 'dark.css') + writeFileSync(distDarkPath, darkCSS, CHARSET) + console.log(' βœ” Generated dark.css') + + console.log('\nβœ… CSS files generated successfully!') + console.log(` Generated 3 CSS files:`) + console.log(' - tokens.css (main, includes both light and dark)') + console.log(' - light.css (light theme only)') + console.log(' - dark.css (dark theme only)') +} catch (error) { + console.error(`\n❌ CSS generation failed: ${error.message}`) + process.exit(1) +} diff --git a/packages/theme/scripts/validate-theme.mjs b/packages/theme/scripts/validate-theme.mjs new file mode 100644 index 00000000..ac4ff0ff --- /dev/null +++ b/packages/theme/scripts/validate-theme.mjs @@ -0,0 +1,157 @@ +/** + * Theme Token Validation Script + * + * Validates the integrity and correctness of theme tokens. + * Checks for: + * - Valid hex color codes + * - Consistent color scales (50-950) + * - Proper semantic token naming + * - Valid CSS variable declarations + * - No duplicate tokens + * - Proper type definitions + */ + +import { readFileSync, existsSync } from 'node:fs' +import { join } from 'node:path' + +const CHARSET = 'utf-8' + +// Token file locations +const TOKENS_DIR = join(process.cwd(), 'src', 'tokens') +const FILES = [ + 'primitives/colors.js', + 'primitives/brand.js', + 'semantic/text.js', + 'semantic/backgrounds.js', + 'semantic/borders.js', + 'tokens-brand.js' +] + +// ─── Validation Functions ─────────────────────────────────────────────────── + +function validateHexColor(value) { + const hexRegex = /^#[0-9A-Fa-f]{6}$|^#[0-9A-Fa-f]{3}$/ + if (!hexRegex.test(value)) { + return false + } + return true +} + +function validateColorScale(colorScale) { + if (!colorScale || typeof colorScale !== 'object') { + return false + } + + // Check if all required shades exist + const requiredShades = [ + '50', + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900', + '950' + ] + for (const shade of requiredShades) { + if (!(shade in colorScale)) { + return false + } + } + + return true +} + +function validateTokenStructure(token, tokenType) { + const errors = [] + + if (!token || typeof token !== 'object') { + errors.push('Token is not an object') + return errors + } + + for (const [key, value] of Object.entries(token)) { + if (key.includes('#') || key.includes('.')) { + errors.push(`Invalid token name: ${key}`) + } + + if (value === null || value === undefined) { + continue + } + + if (key.includes('color') || key.includes('background') || key.includes('text')) { + if (typeof value === 'string' && !validateHexColor(value) && !value.startsWith('var(')) { + errors.push(`Invalid hex color in ${tokenType}.${key}: ${value}`) + } + } + + if (Array.isArray(value)) { + if (tokenType === 'semantic' && !value.every((v) => typeof v === 'object')) { + errors.push(`Invalid structure in ${tokenType}.${key}`) + } + } + } + + return errors +} + +// ─── Main Validation ──────────────────────────────────────────────────────── + +console.log('\nπŸ” Starting theme token validation...\n') + +const validationErrors = [] + +for (const filePath of FILES) { + const fullPath = join(TOKENS_DIR, filePath) + + if (!existsSync(fullPath)) { + console.warn(`⚠ Token file not found: ${filePath}`) + continue + } + + try { + const fileContent = readFileSync(fullPath, CHARSET) + + // Check for CommonJS exports + if (fileContent.includes('module.exports')) { + console.log(` βœ“ ${filePath} uses CommonJS exports`) + } else if (fileContent.includes('export')) { + console.log(` βœ“ ${filePath} uses ES6 exports`) + } else { + console.warn(` ⚠ ${filePath} - No export statements found`) + } + + // Token type detection (heuristic) + let tokenType = 'primitive' + if (filePath.includes('semantic')) { + tokenType = 'semantic' + } else if (filePath.includes('brand')) { + tokenType = 'brand' + } + + console.log(` βœ“ ${filePath} (${tokenType}) - Read successfully`) + } catch (error) { + validationErrors.push(`Failed to read ${filePath}: ${error.message}`) + } +} + +// ─── Summary ──────────────────────────────────────────────────────────────── + +console.log('\n────────────────────────────────────────────────────────') + +if (validationErrors.length === 0) { + console.log('βœ… All theme token files validated successfully!') + console.log(' Found token files:') + console.log(` - ${FILES.length} token files`) + console.log(' All files use valid JavaScript/TypeScript syntax') + console.log() + process.exit(0) +} else { + console.log('❌ Validation failed!') + validationErrors.forEach((error) => console.log(` β€’ ${error}`)) + console.log() + process.exit(1) +} diff --git a/packages/theme/semantic/backgrounds.js b/packages/theme/semantic/backgrounds.js new file mode 100644 index 00000000..e7dd0814 --- /dev/null +++ b/packages/theme/semantic/backgrounds.js @@ -0,0 +1,48 @@ +/** + * SEMANTIC BACKGROUND TOKENS + * + * Generated from figma-reference-tokens-studio. + */ + +import { tokenRef } from '../build/refs.js'; + +export const backgroundSemantic = { + light: { + bgLayer1: tokenRef('brand.surfaces.surface-0'), + bgLayer2: tokenRef('brand.surfaces.surface-50'), + bgBase: tokenRef('brand.surfaces.surface-0'), + bgCanvas: tokenRef('brand.surfaces.surface-100'), + bgLayer1Hover: tokenRef('brand.surfaces.surface-50'), + bgLayer2Hover: tokenRef('brand.surfaces.surface-100'), + bgDangerHover: tokenRef('primitives.red.400'), + bgWarningHover: tokenRef('primitives.yellow.400'), + bgSuccessHover: tokenRef('primitives.green.400'), + bgSuccess: tokenRef('primitives.green.200'), + bgDanger: tokenRef('primitives.red.200'), + bgWarning: tokenRef('primitives.yellow.200'), + bgBackdrop: tokenRef('#00000040'), + bgPrimaryHover: tokenRef('brand.primary.primary-600'), + bgPrimary: tokenRef('brand.primary.primary-500') + }, + dark: { + bgLayer1: tokenRef('brand.surfaces.surface-800'), + bgLayer2: tokenRef('brand.surfaces.surface-700'), + bgBase: tokenRef('brand.surfaces.surface-900'), + bgCanvas: tokenRef('brand.surfaces.surface-950'), + bgLayer1Hover: tokenRef('brand.surfaces.surface-700'), + bgLayer2Hover: tokenRef('brand.surfaces.surface-600'), + bgDangerHover: tokenRef('primitives.red.600'), + bgWarningHover: tokenRef('primitives.yellow.600'), + bgSuccessHover: tokenRef('primitives.green.600'), + bgSuccess: tokenRef('primitives.green.800'), + bgDanger: tokenRef('primitives.red.800'), + bgWarning: tokenRef('primitives.yellow.800'), + bgBackdrop: tokenRef('#00000040'), + bgPrimaryHover: tokenRef('brand.primary.primary-400'), + bgPrimary: tokenRef('brand.primary.primary-500') + }, +}; + +export default { + backgroundSemantic, +}; diff --git a/packages/theme/semantic/borders.js b/packages/theme/semantic/borders.js new file mode 100644 index 00000000..19066715 --- /dev/null +++ b/packages/theme/semantic/borders.js @@ -0,0 +1,46 @@ +/** + * SEMANTIC BORDER TOKENS + * + * Generated from figma-reference-tokens-studio. + */ + +import { tokenRef } from '../build/refs.js'; + +export const borderSemantic = { + light: { + borderBase: tokenRef('brand.surfaces.surface-200'), + borderBaseHover: tokenRef('primitives.base.black'), + borderWarning: tokenRef('primitives.yellow.600'), + borderSuccess: tokenRef('primitives.green.600'), + borderDanger: tokenRef('primitives.red.600'), + borderPrimary: tokenRef('brand.primary.primary-500'), + borderSecondary: tokenRef('primitives.violet.500'), + borderWarningHover: tokenRef('primitives.yellow.500'), + borderSuccessHover: tokenRef('primitives.green.500'), + borderDangerHover: tokenRef('primitives.red.500'), + borderPrimaryHover: tokenRef('brand.primary.primary-600'), + borderSecondaryHover: tokenRef('primitives.violet.600'), + borderAccent: tokenRef('brand.accent.accent-500'), + borderAccentHover: tokenRef('brand.accent.accent-600') + }, + dark: { + borderBase: tokenRef('brand.surfaces.surface-700'), + borderBaseHover: tokenRef('primitives.base.white'), + borderWarning: tokenRef('primitives.yellow.400'), + borderSuccess: tokenRef('primitives.green.400'), + borderDanger: tokenRef('primitives.red.400'), + borderPrimary: tokenRef('brand.primary.primary-500'), + borderSecondary: tokenRef('primitives.violet.500'), + borderWarningHover: tokenRef('primitives.yellow.500'), + borderSuccessHover: tokenRef('primitives.green.500'), + borderDangerHover: tokenRef('primitives.red.500'), + borderPrimaryHover: tokenRef('brand.primary.primary-400'), + borderSecondaryHover: tokenRef('primitives.violet.400'), + borderAccent: tokenRef('brand.accent.accent-500'), + borderAccentHover: tokenRef('brand.accent.accent-400') + }, +}; + +export default { + borderSemantic, +}; diff --git a/packages/theme/semantic/text.js b/packages/theme/semantic/text.js new file mode 100644 index 00000000..d4e5d1d3 --- /dev/null +++ b/packages/theme/semantic/text.js @@ -0,0 +1,78 @@ +/** + * SEMANTIC TEXT TOKENS + * + * Generated from figma-reference-tokens-studio. + */ + +import { tokenRef } from '../build/refs.js'; + +export const textSemantic = { + light: { + textColorBase: tokenRef('primitives.neutral.900'), + textColorMuted: tokenRef('primitives.neutral.600'), + textColorLink: tokenRef('primitives.blue.600'), + textColorCode: tokenRef('primitives.neutral.600'), + textColorMutedHover: tokenRef('primitives.neutral.500'), + textColorLinkHover: tokenRef('primitives.blue.700'), + textColorBaseHover: tokenRef('primitives.neutral.800'), + textColorPrimary: tokenRef('brand.primary.primary-500'), + textColorPrimaryHover: tokenRef('brand.primary.primary-600'), + textColorSecondary: tokenRef('primitives.violet.500'), + textColorSecondaryHover: tokenRef('primitives.violet.600'), + textColorAccent: tokenRef('brand.accent.accent-500'), + textColorAccentHover: tokenRef('brand.accent.accent-600'), + textBase: tokenRef('primitives.neutral.900'), + textMuted: tokenRef('primitives.neutral.600'), + textLink: tokenRef('primitives.blue.600'), + textCode: tokenRef('primitives.neutral.600'), + textMutedHover: tokenRef('primitives.neutral.500'), + textLinkHover: tokenRef('primitives.blue.700'), + textBaseHover: tokenRef('primitives.neutral.800'), + textPrimary: tokenRef('brand.primary.primary-500'), + textPrimaryHover: tokenRef('brand.primary.primary-600'), + textAccent: tokenRef('brand.accent.accent-500'), + textAccentHover: tokenRef('brand.accent.accent-600'), + textDangerHover: tokenRef('primitives.red.500'), + textWarningHover: tokenRef('primitives.yellow.500'), + textSuccessHover: tokenRef('primitives.green.500'), + textSuccess: tokenRef('primitives.green.600'), + textDanger: tokenRef('primitives.red.600'), + textWarning: tokenRef('primitives.yellow.600') + }, + dark: { + textColorBase: tokenRef('primitives.neutral.50'), + textColorMuted: tokenRef('primitives.neutral.400'), + textColorLink: tokenRef('primitives.blue.300'), + textColorCode: tokenRef('primitives.neutral.400'), + textColorMutedHover: tokenRef('primitives.neutral.500'), + textColorLinkHover: tokenRef('primitives.blue.300'), + textColorBaseHover: tokenRef('primitives.neutral.200'), + textColorPrimary: tokenRef('brand.primary.primary-500'), + textColorPrimaryHover: tokenRef('brand.primary.primary-400'), + textColorSecondary: tokenRef('primitives.violet.500'), + textColorSecondaryHover: tokenRef('primitives.violet.400'), + textColorAccent: tokenRef('brand.accent.accent-500'), + textColorAccentHover: tokenRef('brand.accent.accent-400'), + textBase: tokenRef('primitives.neutral.50'), + textMuted: tokenRef('primitives.neutral.400'), + textLink: tokenRef('primitives.blue.300'), + textCode: tokenRef('primitives.neutral.400'), + textMutedHover: tokenRef('primitives.neutral.500'), + textLinkHover: tokenRef('primitives.blue.300'), + textBaseHover: tokenRef('primitives.neutral.200'), + textPrimary: tokenRef('brand.primary.primary-500'), + textPrimaryHover: tokenRef('brand.primary.primary-400'), + textAccent: tokenRef('brand.accent.accent-500'), + textAccentHover: tokenRef('brand.accent.accent-400'), + textDangerHover: tokenRef('primitives.red.500'), + textWarningHover: tokenRef('primitives.yellow.500'), + textSuccessHover: tokenRef('primitives.green.500'), + textSuccess: tokenRef('primitives.green.400'), + textDanger: tokenRef('primitives.red.400'), + textWarning: tokenRef('primitives.yellow.400') + }, +}; + +export default { + textSemantic, +};