diff --git a/apps/frontend-demo/package.json b/apps/frontend-demo/package.json index 2739802b28..8d5fa5f439 100644 --- a/apps/frontend-demo/package.json +++ b/apps/frontend-demo/package.json @@ -21,8 +21,8 @@ "@react-email/components": "^0.3.2", "@tanstack/react-query": "^5.76.1", "@tanstack/react-store": "^0.7.1", - "@zag-js/carousel": "^1.14.0", - "@zag-js/react": "^1.14.0", + "@zag-js/carousel": "^1.22.1", + "@zag-js/react": "^1.22.1", "clsx": "^2.1.1", "dompurify": "^3.2.6", "next": "^15.3.2", diff --git a/apps/frontend-demo/src/tokens/_overrides-components.css b/apps/frontend-demo/src/tokens/_overrides-components.css index ea4ce35070..4368da43ec 100644 --- a/apps/frontend-demo/src/tokens/_overrides-components.css +++ b/apps/frontend-demo/src/tokens/_overrides-components.css @@ -15,27 +15,27 @@ /* === BUTTON === */ /* Change from --alpha-ring to --opacity-ring for all focus rings */ - --color-btn-ring-primary: --alpha( - var(--color-btn-primary) / + --color-button-ring-primary: --alpha( + var(--color-button-primary) / var(--opacity-ring) ); - --color-btn-ring-secondary: --alpha( - var(--color-btn-secondary) / + --color-button-ring-secondary: --alpha( + var(--color-button-secondary) / var(--opacity-ring) ); - --color-btn-ring-tertiary: --alpha( - var(--color-btn-tertiary) / + --color-button-ring-tertiary: --alpha( + var(--color-button-tertiary) / var(--opacity-ring) ); - --color-btn-ring-warning: --alpha( - var(--color-btn-warning) / + --color-button-ring-warning: --alpha( + var(--color-button-warning) / var(--opacity-ring) ); - --color-btn-ring-danger: --alpha( - var(--color-btn-danger) / + --color-button-ring-danger: --alpha( + var(--color-button-danger) / var(--opacity-ring) ); - --color-btn-text-secondary: var(--color-fg-dark); + --color-button-text-secondary: var(--color-fg-dark); /* === INPUT === */ /* Different padding for small inputs */ diff --git a/apps/frontend-demo/src/tokens/_overrides.css b/apps/frontend-demo/src/tokens/_overrides.css index 524dd5f576..317457221e 100644 --- a/apps/frontend-demo/src/tokens/_overrides.css +++ b/apps/frontend-demo/src/tokens/_overrides.css @@ -279,8 +279,8 @@ ); /* Borders */ - --color-stroke-primary: var(--color-slate-200); - --color-stroke-secondary: var(--color-slate-400); + --color-border-primary: var(--color-slate-200); + --color-border-secondary-alt: var(--color-slate-400); --color-border: light-dark(var(--color-slate-200), var(--color-slate-800)); --color-border-neutral: light-dark( var(--color-slate-300), @@ -331,7 +331,7 @@ var(--opacity-disabled) ); --color-disabled-border: --alpha( - var(--color-stroke-primary) / + var(--color-border-primary) / var(--opacity-disabled) ); --color-disabled-fill: --alpha( diff --git a/apps/frontend-demo/src/tokens/app-components/molecules/_cart-preview.css b/apps/frontend-demo/src/tokens/app-components/molecules/_cart-preview.css index bde2838dd5..e900c27e08 100644 --- a/apps/frontend-demo/src/tokens/app-components/molecules/_cart-preview.css +++ b/apps/frontend-demo/src/tokens/app-components/molecules/_cart-preview.css @@ -1,10 +1,10 @@ @theme static { /* === CART PREVIEW COLORS === */ - --color-cart-preview-item-border: var(--color-stroke-secondary); + --color-cart-preview-item-border: var(--color-border-secondary); --color-cart-preview-item-image-bg: var(--color-fill); --color-cart-preview-fg: var(--color-fg-primary); --color-cart-preview-fg-secondary: var(--color-fg-secondary); - --color-cart-preview-footer-border: var(--color-stroke-secondary); + --color-cart-preview-footer-border: var(--color-border-secondary); /* === SPACING === */ --spacing-cart-preview: var(--spacing-xs); diff --git a/apps/frontend-demo/src/tokens/app-components/molecules/_navigation.css b/apps/frontend-demo/src/tokens/app-components/molecules/_navigation.css index 9ad7f9f856..294fe67d33 100644 --- a/apps/frontend-demo/src/tokens/app-components/molecules/_navigation.css +++ b/apps/frontend-demo/src/tokens/app-components/molecules/_navigation.css @@ -12,7 +12,7 @@ /* Navigation submenu */ --color-nav-submenu-bg: var(--color-surface); --color-nav-submenu-item-hover: var(--color-surface-hover); - --color-nav-submenu-border: var(--color-stroke-primary); + --color-nav-submenu-border: var(--color-border-primary); --shadow-nav-submenu: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1); diff --git a/apps/frontend-demo/src/tokens/app-components/molecules/_product-card.css b/apps/frontend-demo/src/tokens/app-components/molecules/_product-card.css index 6c8b955913..d95e302afc 100644 --- a/apps/frontend-demo/src/tokens/app-components/molecules/_product-card.css +++ b/apps/frontend-demo/src/tokens/app-components/molecules/_product-card.css @@ -28,28 +28,28 @@ /* === PRODUCT CARD BUTTONS ===*/ /*Add to cart button*/ - --color-btn-cart: var(--color-btn-primary); - --color-btn-cart-hover: oklch( - from var(--color-btn-cart) calc(l + var(--state-hover)) c h + --color-button-cart: var(--color-button-primary); + --color-button-cart-hover: oklch( + from var(--color-button-cart) calc(l + var(--state-hover)) c h ); - --color-btn-cart-fg: var(--color-btn-text); + --color-button-cart-fg: var(--color-button-fg); /* Detail button */ - --color-btn-detail: var(--color-btn-secondary); - --color-btn-detail-hover: oklch( - from var(--color-btn-detail) calc(l + var(--state-hover)) c h + --color-button-detail: var(--color-button-secondary); + --color-button-detail-hover: oklch( + from var(--color-button-detail) calc(l + var(--state-hover)) c h ); - --color-btn-detail-fg: var(--color-btn-text); + --color-button-detail-fg: var(--color-button-fg); /* wishlist button */ - --color-btn-wishlist: var(--color-btn-tertiary); - --color-btn-wishlist-hover: oklch( - from var(--color-btn-wishlist) calc(l + var(--state-hover)) c h + --color-button-wishlist: var(--color-button-tertiary); + --color-button-wishlist-hover: oklch( + from var(--color-button-wishlist) calc(l + var(--state-hover)) c h ); - --color-btn-wishlist-fg: var(--color-btn-text); + --color-button-wishlist-fg: var(--color-button-fg); /* === PRODUCT CARD IMAGE === */ --aspect-pc-image: var(--aspect-ratio-square); diff --git a/apps/frontend-demo/src/tokens/app-components/organisms/_header.css b/apps/frontend-demo/src/tokens/app-components/organisms/_header.css index e866f73416..6575c5a641 100644 --- a/apps/frontend-demo/src/tokens/app-components/organisms/_header.css +++ b/apps/frontend-demo/src/tokens/app-components/organisms/_header.css @@ -14,7 +14,7 @@ --color-header-mobile-menu-hover: var(--color-fill-hover); --color-header-mobile-menu-text: var(--color-fg-secondary); --color-header-mobile-menu-text-hover: var(--color-fg-primary); - --color-header-divider: var(--color-stroke-secondary); + --color-header-divider: var(--color-border-secondary); /* === HEADER SPACING === */ --spacing-header-container-x: var(--spacing-xs); diff --git a/libs/ui/.storybook/main.ts b/libs/ui/.storybook/main.ts index 929ea93db0..e57e0ac197 100644 --- a/libs/ui/.storybook/main.ts +++ b/libs/ui/.storybook/main.ts @@ -14,6 +14,7 @@ const config: StorybookConfig = { '../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)', ], + staticDirs: ['../stories/assets'], addons: [ '@storybook/addon-onboarding', '@storybook/addon-links', diff --git a/libs/ui/.storybook/preview.ts b/libs/ui/.storybook/preview.ts index 4613e41bef..dc949205ca 100644 --- a/libs/ui/.storybook/preview.ts +++ b/libs/ui/.storybook/preview.ts @@ -11,6 +11,19 @@ const preview: Preview = { date: /Date$/i, }, }, + options: { + storySort: { + order: [ + 'Atoms', + 'Molecules', + 'Organisms', + 'Templates', + 'Pages', + 'Overview', + '*', + ], + }, + }, }, decorators: [ withThemeByClassName({ diff --git a/libs/ui/CLAUDE.md b/libs/ui/CLAUDE.md index 2bb2105b70..36e2a8b74f 100644 --- a/libs/ui/CLAUDE.md +++ b/libs/ui/CLAUDE.md @@ -8,15 +8,9 @@ This is a React 19 + Tailwind v4 UI library using atomic design principles. We e - **tailwind-variants** for component styling - **Zag.js** for complex interactive components -**IMPORTANT**: Never mention or use `tailwind.config`, `forwardRef`, or `useCallback`. +**CRITICAL**: Never mention or use `tailwind.config`, `forwardRef`, or `useCallback`. -## Required Tools - -**YOU MUST** use these tools when implementing components: -- **MCP Quillopy**: For studying Tailwind v4, tailwind-variants, and React 19 documentation -- **Context7**: For researching Zag.js patterns and component implementations - -## File Structure +## Architecture Overview ``` libs/ui/src/ @@ -24,112 +18,103 @@ libs/ui/src/ ├── molecules/ # Composite components (Dialog, Combobox, Toast) ├── organisms/ # Complex sections (rarely used in this library) ├── tokens/ # Design tokens -│ └── components/ # Component-specific tokens (_button.css, _combobox.css) +│ ├── components/ +│ │ ├── atoms/ # Atomic component tokens +│ │ └── molecules/ # Molecular component tokens +│ └── _semantic.css # Base semantic tokens └── utils/ # Shared utilities (tv setup) ``` -## CSS Token System +## Component Development Workflow -### Token Definition Rules +### Creating New Component (Required Process) -**IMPORTANT**: All component tokens must be defined in `@theme static` blocks: +1. **Research Phase**: + - **Context7**: Study Zag.js component documentation for interactive components + - **MCP Quillopy**: Research tailwind-variants patterns and Tailwind v4 features -```css -/* libs/ui/src/tokens/components/_component-name.css */ -@theme static { - /* Category-Component-Property-State pattern */ - --color-button-bg: var(--color-primary); - --color-button-bg-hover: oklch(from var(--color-button-bg) calc(l + var(--state-hover)) c h); - --spacing-button-sm: var(--spacing-xs); -} -``` +2. **File Creation**: + - Component: `libs/ui/src/[atoms|molecules]/component-name.tsx` + - Tokens: `libs/ui/src/tokens/components/[atoms|molecules]/_component-name.css` + - Story: `libs/ui/stories/component-name.stories.tsx` -### Allowed Token Prefixes +3. **Token Definition** (follow rules below) -**YOU MUST** use these prefixes for design tokens: +4. **Component Implementation** using tv() pattern -- `--color-`: All color values -- `--text-`: Font sizes (must map to Tailwind text sizes) -- `--font-weight-`: Font weights (100-900 or semantic) -- `--border-`: Border width, style, color -- `--opacity-`: Transparency values (0-100%) -- `--spacing-`: Padding, margin, gap, AND width/height values that need max/min variants -- `--width-`: Width values (only when you don't need max/min variants) -- `--height-`: Height values (only when you don't need max/min variants) -- `--gap-`: Flex/grid gaps -- `--padding-`: Padding values -- `--margin-`: Margin values -- `--radius-`: Border radius -- `--shadow-`: Box shadows +5. **Story Creation** covering all variants and states -**IMPORTANT**: Use `--spacing-` prefix for any width/height values that need `max-w`/`min-w`/`max-h`/`min-h` utilities! +## Token System Rules -### Forbidden Patterns +See comprehensive token documentation in [`token-contribution.md`](./token-contribution.md) -- ❌ `--grid-cols-product-grid-base` (too specific) -- ❌ `--layout-*` (use specific properties like `--spacing-` or `--width-`) -- ❌ `--component-specific-anything` (aim for generic, reusable token names) +### Key Points: +- **Two-layer token strategy**: Reference layer → Derived tokens +- **Strict naming conventions**: `--[prefix]-[component]-[part?]-[property]-[state?]` +- **Required suffixes**: All colors must have `-bg`, `-fg`, or `-border` +- **No abbreviations**: Use full names (`button` not `btn`) +- **Validation tools**: Run `pnpm validate:tokens` to check compliance -### Token Usage in Components +### Quick Token Usage in Components: ```typescript // ✅ CORRECT - Use Tailwind classes that reference your tokens -'bg-button-bg' // Uses --color-button-bg -'text-button-fg' // Uses --color-button-fg -'p-button-sm' // Uses --spacing-button-sm +'bg-button-bg-primary' // Uses --color-button-bg-primary +'text-button-fg-primary' // Uses --color-button-fg-primary +'p-button-padding' // Uses --spacing-button-padding // ❌ WRONG - Never use arbitrary values -'bg-[var(--color-button-bg)]' -'p-[var(--spacing-button-sm)]' +'bg-[var(--color-button-bg-primary)]' // Don't use arbitrary values +'p-[1rem]' // Use token classes instead ``` -## Component Development Workflow +## Component Implementation Patterns -### 1. Creating New Component +### Basic Component Structure -1. **Research with MCP Quillopy/Context7**: - - For Zag.js components: Study the specific component documentation - - For styling: Research tailwind-variants patterns +```typescript +import { tv } from '../utils' + +const componentVariants = tv({ + slots: { + root: ['flex', 'bg-component-bg', 'data-[state=open]:bg-component-bg-open'], + content: ['text-component-fg', 'p-component-padding'] + }, + variants: { + variant: { + primary: { + root: 'bg-component-bg-primary', + content: 'text-component-fg-primary' + }, + secondary: { + root: 'bg-component-bg-secondary', + content: 'text-component-fg-secondary' + } + }, + size: { + sm: { root: 'p-component-sm', content: 'text-component-sm' }, + md: { root: 'p-component-md', content: 'text-component-md' } + } + }, + defaultVariants: { + variant: 'primary', + size: 'md' + } +}) + +export function Component({ variant, size, ref, ...props }) { + const { root, content } = componentVariants({ variant, size }) + return ( +
+
Content
+
+ ) +} +``` -2. **Create Files**: - - Component: `libs/ui/src/[atoms|molecules]/component-name.tsx` - - Tokens: `libs/ui/src/tokens/components/_component-name.css` - - Story: `libs/ui/stories/component-name.stories.tsx` +### Zag.js Interactive Components -3. **Define Tokens** (follow prefix rules above) - -4. **Implement Component**: - ```typescript - import { tv } from '../utils' - - const componentVariants = tv({ - slots: { - root: ['flex', 'bg-component-bg', 'data-[state=open]:bg-component-bg-open'], - content: ['text-component-fg', 'p-component'] - }, - variants: { - size: { - sm: { root: 'p-component-sm', content: 'text-component-sm' }, - md: { root: 'p-component-md', content: 'text-component-md' } - } - } - }) - - export function Component({ size = 'md', ref, ...props }) { - const { root, content } = componentVariants({ size }) - return ( -
-
Content
-
- ) - } - ``` - -5. **Create Comprehensive Stories** covering all variants and states - -## Zag.js Integration Pattern - -**IMPORTANT**: Always use MCP Quillopy/Context7 to study Zag.js documentation for the specific component. +**REQUIRED**: Use Context7 to study specific Zag.js component documentation. ```typescript import * as component from '@zag-js/component-name' @@ -153,40 +138,138 @@ export function Component(props: ComponentProps) { } ``` -## State-Based Styling +### State-Based Styling Use data attributes for dynamic styling: ```typescript // State attributes from Zag.js or custom 'data-[state=open]:rotate-180' -'data-[highlighted]:bg-item-hover' +'data-[highlighted]:bg-component-bg-hover' 'data-[disabled]:opacity-disabled' -'data-[validation=error]:border-danger' +'data-[validation=error]:border-component-border-danger' +``` + +## Required Tools Integration + +**YOU MUST use these tools**: +- **Context7**: For researching Zag.js patterns and component implementations +- **MCP Quillopy**: For studying Tailwind v4, tailwind-variants, and React 19 documentation + +**When to use each**: +- Creating interactive components → Context7 first +- Styling questions → MCP Quillopy +- Complex state management → Context7 for Zag.js patterns + +## Development Commands + +```bash +# Component development +bunx nx run ui:storybook # Start Storybook for component preview +bunx nx run ui:build # Build UI library + +# Token validation +pnpm validate:tokens # Validate token naming and structure +pnpm check:unused-tokens # Find unused token definitions ``` -## Common Pitfalls to Avoid +## Quality Standards -1. **Token Usage**: - - ❌ Using arbitrary values: `bg-[#ff0000]` - - ✅ Using token-based classes: `bg-button-danger` +### Component Validation Checklist + +Before considering a component complete: -2. **React 19 Patterns**: +- [ ] **Token file structure**: Follows mandatory section order +- [ ] **Token naming**: All colors have explicit suffixes (-bg, -fg) +- [ ] **Reference layer**: Implemented for all themeable properties +- [ ] **Component implementation**: Uses tv() with token-based classes +- [ ] **No arbitrary values**: All styling uses semantic tokens +- [ ] **Story coverage**: All variants and states documented +- [ ] **Tool usage**: Used Context7/MCP Quillopy for research +- [ ] **React 19 patterns**: No forwardRef, ref as prop +- [ ] **State styling**: Data attributes for dynamic states +- [ ] **No forbidden patterns**: No tailwind.config mentions + +### Common Pitfalls to Avoid + +1. **Token Naming Errors**: + - ❌ `--color-button` (missing -bg suffix) + - ❌ `--color-btn-primary` (use 'button' not 'btn') + - ❌ `--color-button-text` (use -fg not -text) + - ✅ `--color-button-bg-primary` + +2. **React 19 Violations**: - ❌ `forwardRef((props, ref) => ...)` + - ❌ `useCallback(() => {}, [])` - ✅ `function Component({ ref, ...props }: Props & { ref?: Ref })` -3. **Tailwind v4**: +3. **Tailwind v4 Violations**: - ❌ Mentioning `tailwind.config.js` - - ✅ Using `@theme static` blocks + - ❌ Using arbitrary values `bg-[#ff0000]` + - ✅ Using `@theme static` blocks and token classes -## Validation Checklist +4. **Architecture Violations**: + - ❌ Direct semantic token usage: `bg-primary` + - ❌ Missing reference layer + - ✅ Component-specific tokens: `bg-button-bg-primary` -Before considering a component complete: +## Storybook Integration + +```typescript +// component-name.stories.tsx +export default { + title: 'Atoms/ComponentName', + component: Component, + parameters: { + docs: { description: { component: 'Component description...' } } + } +} + +// Cover ALL variants and states +export const AllVariants: Story = { + render: () => ( +
+ + + +
+ ) +} +``` -- [ ] Token file follows all prefix rules -- [ ] Component uses `tv()` with token-based classes -- [ ] No arbitrary values in styling -- [ ] Story file covers all variants and states -- [ ] Used MCP Quillopy/Context7 for documentation research -- [ ] No React 18 patterns (forwardRef, useCallback) -- [ ] Data attributes used for state-based styling \ No newline at end of file +## File Organization Rules + +### Component Files +- **Atoms**: `src/atoms/component-name.tsx` +- **Molecules**: `src/molecules/component-name.tsx` +- **Stories**: `stories/component-name.stories.tsx` + +### Token Files +- **Atoms**: `src/tokens/components/atoms/_component-name.css` +- **Molecules**: `src/tokens/components/molecules/_component-name.css` + +### Naming Conventions +- **Files**: kebab-case (`component-name.tsx`) +- **Components**: PascalCase (`ComponentName`) +- **Tokens**: kebab-case (`--color-component-name-bg`) + +## Do Not Section + +**NEVER**: +- Mention `tailwind.config.js` or `tailwind.config.ts` +- Use `forwardRef` or `useCallback` (React 19 doesn't need them) +- Use arbitrary values in styling (`bg-[#ff0000]`) +- Create tokens without proper prefixes +- Skip the reference layer in token architecture +- Use abbreviations in component names (`btn`, `pc`, etc.) +- Create components without comprehensive stories +- Implement interactive components without studying Zag.js docs via Context7 + +**ALWAYS**: +- Use `@theme static` blocks for token definitions +- Implement two-layer token strategy (reference + derived) +- Use data attributes for state-based styling +- Research with Context7 for Zag.js components +- Research with MCP Quillopy for Tailwind v4 features +- Follow the mandatory token file structure +- Create comprehensive Storybook stories \ No newline at end of file diff --git a/libs/ui/eslint.config.js b/libs/ui/eslint.config.js index 655b1a092c..de276d610f 100644 --- a/libs/ui/eslint.config.js +++ b/libs/ui/eslint.config.js @@ -1,6 +1,13 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' import tsParser from '@typescript-eslint/parser' import tailwind from 'eslint-plugin-tailwindcss' +const tokensCssAbsolutePath = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + 'src/tokens/index.css' +) + export default [ { files: ['**/*.{js,jsx,ts,tsx}'], @@ -17,7 +24,7 @@ export default [ settings: { tailwindcss: { // For TailwindCSS v4 - point to CSS config file - config: 'D:\\Work\\new-engine\\libs\\ui\\src\\tokens\\index.css', + config: tokensCssAbsolutePath, }, }, plugins: { diff --git a/libs/ui/package.json b/libs/ui/package.json index b2a5478068..cb4a3c083f 100644 --- a/libs/ui/package.json +++ b/libs/ui/package.json @@ -39,32 +39,34 @@ "storybook": "storybook dev", "lint:tailwind": "eslint \"**/*.{js,jsx,ts,tsx}\" --config eslint.config.js", "lint:tailwind:fix": "eslint \"**/*.{js,jsx,ts,tsx}\" --config eslint.config.js --fix", - "check:unused-tokens": "node scripts/check-unused-tokens.js", - "prepare": "pnpm build" + "prepare": "pnpm build", + "check:unused-tokens": "bun scripts/check-unused-tokens.js", + "validate:tokens": "bun scripts/validate-token-usage.js && bun scripts/validate-token-definitions.js", + "validate:token-usage": "bun scripts/validate-token-usage.js", + "validate:token-definitions": "bun scripts/validate-token-definitions.js" }, - "dependencies": { - "@zag-js/accordion": "^1.15.0", - "@zag-js/carousel": "^1.15.0", - "@zag-js/checkbox": "^1.15.0", - "@zag-js/combobox": "^1.15.0", - "@zag-js/dialog": "^1.15.0", - "@zag-js/i18n-utils": "^1.15.0", - "@zag-js/number-input": "^1.15.0", - "@zag-js/pagination": "^1.15.0", - "@zag-js/popover": "^1.15.0", - "@zag-js/rating-group": "^1.15.0", - "@zag-js/react": "^1.15.0", - "@zag-js/select": "^1.15.0", - "@zag-js/slider": "^1.15.0", - "@zag-js/steps": "^1.15.0", - "@zag-js/menu": "^1.15.0", - "@zag-js/switch": "^1.15.0", - "@zag-js/tabs": "^1.15.0", - "@zag-js/toast": "^1.15.0", - "@zag-js/tooltip": "^1.15.0", - "@zag-js/tree-view": "^1.15.0", - "tailwind-variants": "^1.0.0" + "@zag-js/accordion": "^1.22.1", + "@zag-js/carousel": "^1.22.1", + "@zag-js/checkbox": "^1.22.1", + "@zag-js/combobox": "^1.22.1", + "@zag-js/dialog": "^1.22.1", + "@zag-js/i18n-utils": "^1.22.1", + "@zag-js/menu": "^1.22.1", + "@zag-js/number-input": "^1.22.1", + "@zag-js/pagination": "^1.22.1", + "@zag-js/popover": "^1.22.1", + "@zag-js/rating-group": "^1.22.1", + "@zag-js/react": "^1.22.1", + "@zag-js/select": "^1.22.1", + "@zag-js/slider": "^1.22.1", + "@zag-js/steps": "^1.22.1", + "@zag-js/switch": "^1.22.1", + "@zag-js/tabs": "^1.22.1", + "@zag-js/toast": "^1.22.1", + "@zag-js/tooltip": "^1.22.1", + "@zag-js/tree-view": "^1.22.1", + "tailwind-variants": "^3.1.0" }, "devDependencies": { "@eslint/js": "^9.0.0", @@ -87,8 +89,9 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "eslint": "^9.0.0", - "eslint-plugin-tailwindcss": "npm:@hyoban/eslint-plugin-tailwindcss@4.0.0-alpha.12", + "eslint-plugin-tailwindcss": "beta", "glob": "^11.0.2", + "next": "^15.5.0", "react": "^19.1.0", "react-dom": "^19.1.0", "storybook": "^8.6.12", diff --git a/libs/ui/scripts/validate-token-definitions.js b/libs/ui/scripts/validate-token-definitions.js new file mode 100755 index 0000000000..b5f7d57bab --- /dev/null +++ b/libs/ui/scripts/validate-token-definitions.js @@ -0,0 +1,507 @@ +#!/usr/bin/env node + +/** + * Token Definition Validation Script (optimized) + * + * - Single-pass indexing of token CSS files + * - Single-pass scanning of component files + * - Dependency closure via forward BFS + * - Optional --profile timings + */ + +import { existsSync } from 'node:fs' +import fs from 'node:fs/promises' +import path from 'node:path' +import { performance } from 'node:perf_hooks' +import { fileURLToPath } from 'node:url' +import { globSync } from 'glob' + +const ROOT = path.resolve(fileURLToPath(new URL('.', import.meta.url)), '..') + +// Configuration for validation +const CONFIG = { + // Tokens to always consider "used" (whitelist) + whitelistPatterns: [ + /^--color-primary$/, + /^--color-secondary$/, + /^--color-danger$/, + /^--color-warning$/, + /^--color-success$/, + /^--color-info$/, + /^--spacing-\d{2,3}$/, + /^--text-(xs|sm|md|lg|xl)$/, + /^--radius-(sm|md|lg)$/, + // Base system tokens + /^--color-.*-(50|100|200|300|400|500|600|700|800|900)$/, + /^--state-(hover|focus|active|disabled)$/, + ], + + // Patterns to ignore completely + ignorePatterns: [/^--tw-/, /_test$/, /_debug$/], + + // File patterns to exclude from usage scanning + excludeFiles: [ + '**/*.stories.tsx', + '**/*.test.tsx', + '**/*.spec.tsx', + '**/node_modules/**', + ], + + // Token CSS glob + tokenCssGlob: 'src/tokens/components/**/*.css', +} + +// Simple stopwatch profiling +function profiled(enabled) { + const marks = new Map() + return { + mark(label) { + if (!enabled) return + marks.set(label, performance.now()) + }, + end(label) { + if (!enabled) return 0 + const start = marks.get(label) ?? performance.now() + const delta = performance.now() - start + marks.set(label, performance.now()) + return delta + }, + } +} + +function isWhitelisted(tokenName) { + return CONFIG.whitelistPatterns.some((p) => p.test(tokenName)) +} + +function shouldIgnoreToken(tokenName) { + return CONFIG.ignorePatterns.some((p) => p.test(tokenName)) +} + +// Map CSS token to possible Tailwind utility classes +function tokenToUtilityClasses(tokenName) { + const classes = new Set() + const tokenParts = tokenName.slice(2).split('-') + if (tokenParts.length < 2) return classes + + const primaryNamespace = tokenParts[0] + const subNamespace = tokenParts.length > 2 ? tokenParts[1] : null + const key = tokenParts.slice(subNamespace ? 2 : 1).join('-') + + const mappings = { + color: [ + 'bg', + 'text', + 'border', + 'outline', + 'decoration', + 'shadow', + 'inset-shadow', + 'ring', + 'ring-offset', + 'inset-ring', + 'accent', + 'caret', + 'fill', + 'stroke', + ], + spacing: [ + 'p', + 'px', + 'py', + 'pt', + 'pr', + 'pb', + 'pl', + 'ps', + 'pe', + 'm', + 'mx', + 'my', + 'mt', + 'mr', + 'mb', + 'ml', + 'ms', + 'me', + '-m', + '-mx', + '-my', + '-mt', + '-mr', + '-mb', + '-ml', + '-ms', + '-me', + 'w', + 'h', + 'min-w', + 'min-h', + 'max-w', + 'max-h', + 'inset', + 'inset-x', + 'inset-y', + 'top', + 'right', + 'bottom', + 'left', + 'start', + 'end', + '-inset', + '-inset-x', + '-inset-y', + '-top', + '-right', + '-bottom', + '-left', + '-start', + '-end', + 'gap', + 'gap-x', + 'gap-y', + 'space-x', + 'space-y', + ], + padding: ['p', 'px', 'py', 'pt', 'pr', 'pb', 'pl', 'ps', 'pe'], + margin: [ + 'm', + 'mx', + 'my', + 'mt', + 'mr', + 'mb', + 'ml', + 'ms', + 'me', + '-m', + '-mx', + '-my', + '-mt', + '-mr', + '-mb', + '-ml', + '-ms', + '-me', + ], + gap: ['gap'], + width: ['w', 'min-w', 'max-w'], + height: ['h', 'min-h', 'max-h'], + text: ['text'], + font: ['font'], + tracking: ['tracking'], + leading: ['leading'], + radius: ['rounded'], + shadow: ['shadow'], + 'inset-shadow': ['inset-shadow'], + 'drop-shadow': ['drop-shadow'], + blur: ['blur'], + perspective: ['perspective'], + aspect: ['aspect'], + ease: ['ease'], + animate: ['animate'], + opacity: ['opacity'], + border: ['border'], + z: [], + arrow: [], + tree: [], + textarea: [], + tooltip: [], + } + + let namespace = primaryNamespace + let tokenKey = key + if (primaryNamespace === 'font' && subNamespace === 'weight') { + namespace = 'font-weight' + } else if (subNamespace && mappings[`${primaryNamespace}-${subNamespace}`]) { + namespace = `${primaryNamespace}-${subNamespace}` + } else if (subNamespace) { + tokenKey = `${subNamespace}-${key}` + } + + const customPropertyPrefixes = [ + 'arrow', + 'tree', + 'tooltip', + 'textarea', + 'z', + 'opacity-bg', + 'opacity-borderless', + 'spacing-translate', + 'spacing-menu-submenu', + ] + if ( + customPropertyPrefixes.some((prefix) => tokenName.includes(`--${prefix}`)) + ) { + return classes + } + + const prefixes = mappings[namespace] || mappings[primaryNamespace] || [] + for (const prefix of prefixes) { + if (namespace === 'font-weight') { + classes.add(`font-${tokenKey}`) + } else { + classes.add(`${prefix}-${tokenKey}`) + } + } + return classes +} + +// Build indices from token CSS files in a single pass +async function buildTokenIndices() { + const files = globSync(CONFIG.tokenCssGlob) + const defs = new Map() // token -> { value, file, line } + const dependencyGraph = new Map() // token -> Set + const cssUsage = new Map() // token -> Set + + await Promise.all( + files.map(async (file) => { + const abs = path.join(ROOT, file) + if (!existsSync(abs)) return + try { + const content = await fs.readFile(abs, 'utf8') + const lines = content.split('\n') + + // 1) Parse token definitions and dependency edges + const tokenRegex = /--([\w-]+)\s*:\s*([^;]+);/g + let m + while ((m = tokenRegex.exec(content)) !== null) { + const name = `--${m[1]}` + const value = m[2].trim() + const line = content.substring(0, m.index).split('\n').length + defs.set(name, { value, file, line }) + + // Extract dependencies from value + const deps = new Set() + for (const vm of value.matchAll(/var\(\s*(--[\w-]+)/g)) { + deps.add(vm[1]) + } + for (const om of value.matchAll(/oklch\([^)]*var\(\s*(--[\w-]+)/g)) { + deps.add(om[1]) + } + dependencyGraph.set(name, deps) + } + + // 2) Index var() usage on non-definition lines as direct CSS usage + const defLine = new Set() + for (const [token, data] of defs) { + if (data.file === file) defLine.add(data.line) + } + + for (let i = 0; i < lines.length; i++) { + const lineNo = i + 1 + if (defLine.has(lineNo)) continue + const line = lines[i] + if (!line.includes('var(')) continue + for (const vm of line.matchAll(/var\(\s*(--[\w-]+)/g)) { + const t = vm[1] + if (!cssUsage.has(t)) cssUsage.set(t, new Set()) + cssUsage.get(t).add(`${file}:${lineNo} (CSS property)`) // direct usage + } + } + } catch (err) { + console.error(`💥 Failed to process ${file}:`, err?.message || err) + } + }) + ) + + // Ensure every token key exists in maps + for (const token of defs.keys()) { + if (!dependencyGraph.has(token)) dependencyGraph.set(token, new Set()) + if (!cssUsage.has(token)) cssUsage.set(token, new Set()) + } + + return { defs, dependencyGraph, cssUsage } +} + +// Build component indices in a single pass +async function buildComponentIndices(classToTokens, knownTokens) { + const files = globSync('src/**/*.{ts,tsx}', { + cwd: ROOT, + ignore: CONFIG.excludeFiles, + }) + const componentVarUsage = new Map() // token -> Set + const classUsageTokens = new Set() // tokens used via classes + + const normalizeClass = (raw) => { + // Strip variant prefixes like sm:, hover:, data-[...]: etc. + if (!raw) return '' + const parts = String(raw).split(':') + const core = parts[parts.length - 1] + return core + } + + await Promise.all( + files.map(async (file) => { + if (!existsSync(file)) return + try { + const content = await fs.readFile(file, 'utf8') + + // 1) Direct var(--token) usage in TSX + for (const m of content.matchAll(/var\(\s*(--[\w-]+)/g)) { + const t = m[1] + if (!componentVarUsage.has(t)) componentVarUsage.set(t, new Set()) + const lineNo = content.substring(0, m.index).split('\n').length + componentVarUsage + .get(t) + .add(`${file}:${lineNo} (component var() reference)`) + } + + // 1b) Arbitrary utilities referencing tokens directly, e.g. border-(length:--token) + for (const m of content.matchAll(/--[a-z0-9-]+/gi)) { + const t = m[0] + if (knownTokens.has(t)) { + classUsageTokens.add(t) + } + } + + // 2) Class-based usage: scan whole file for class-like tokens + // Capture words with at least one hyphen, optionally with variant prefixes like sm:, hover:, data-[...]: + const classLike = + /(^|[^A-Za-z0-9_-])([A-Za-z0-9_-]+(?::[A-Za-z0-9_[\]-]+)*-[A-Za-z0-9_[\]-]+)/g + for (const m of content.matchAll(classLike)) { + const raw = m[2] + const cls = normalizeClass(raw) + if (!cls) continue + const tokens = classToTokens.get(cls) + if (!tokens) continue + for (const t of tokens) classUsageTokens.add(t) + } + } catch { + // ignore unreadable files + } + }) + ) + + return { componentVarUsage, classUsageTokens } +} + +function computeClassMaps(tokens) { + const tokenToClasses = new Map() + const classToTokens = new Map() + for (const token of tokens) { + const classes = tokenToUtilityClasses(token) + tokenToClasses.set(token, classes) + for (const c of classes) { + if (!classToTokens.has(c)) classToTokens.set(c, new Set()) + classToTokens.get(c).add(token) + } + } + return { tokenToClasses, classToTokens } +} + +function propagateUsage(initialUsed, dependencyGraph) { + const used = new Set(initialUsed) + const queue = [...initialUsed] + while (queue.length) { + const cur = queue.shift() + const deps = dependencyGraph.get(cur) + if (!deps) continue + for (const d of deps) { + if (!used.has(d)) { + used.add(d) + queue.push(d) + } + } + } + return used +} + +async function validateTokenDefinitions({ profile = false } = {}) { + const p = profiled(profile) + p.mark('total') + console.log('🔍 Analyzing token definitions and usage...') + + // 1) Token indices + p.mark('tokens') + const { defs, dependencyGraph, cssUsage } = await buildTokenIndices() + const allTokens = Array.from(defs.keys()) + if (profile) console.log(`⏱️ tokens: ${p.end('tokens').toFixed(1)}ms`) + console.log(`📋 Found ${allTokens.length} total tokens\n`) + + // 2) Class maps from tokens + p.mark('classmaps') + const { classToTokens } = computeClassMaps(allTokens) + if (profile) console.log(`⏱️ class maps: ${p.end('classmaps').toFixed(1)}ms`) + + // 3) Component indices + p.mark('components') + const { componentVarUsage, classUsageTokens } = await buildComponentIndices( + classToTokens, + new Set(allTokens) + ) + if (profile) console.log(`⏱️ components: ${p.end('components').toFixed(1)}ms`) + + // 4) Seed used set + const usedDirect = new Set() + for (const t of allTokens) { + if (isWhitelisted(t)) usedDirect.add(t) + } + // Direct CSS var() usage (outside token defs) + for (const [t, locs] of cssUsage) { + if (locs.size > 0) usedDirect.add(t) + } + // Component var() usage + for (const [t, locs] of componentVarUsage) { + if (locs.size > 0) usedDirect.add(t) + } + // Class usage + for (const t of classUsageTokens) usedDirect.add(t) + + // 5) Propagate through dependencies (forward) + p.mark('closure') + const usedTokens = propagateUsage(usedDirect, dependencyGraph) + if (profile) console.log(`⏱️ closure: ${p.end('closure').toFixed(1)}ms`) + + // 6) Classify + const unusedTokens = [] + for (const t of allTokens) { + if (shouldIgnoreToken(t)) continue + if (!usedTokens.has(t)) { + const d = defs.get(t) + unusedTokens.push({ name: t, file: d.file, line: d.line, value: d.value }) + } + } + + // Report results + console.log(`\n📊 Validation Summary:`) + console.log(` Total tokens: ${allTokens.length}`) + console.log(` Used tokens: ${allTokens.length - unusedTokens.length}`) + console.log(` Unused tokens: ${unusedTokens.length}`) + + if (unusedTokens.length === 0) { + console.log('\n✅ All tokens are being used!') + if (profile) console.log(`⏱️ total: ${p.end('total').toFixed(1)}ms`) + return true + } + + console.log(`\n⚠️ Found ${unusedTokens.length} potentially unused tokens:\n`) + const byFile = new Map() + for (const tok of unusedTokens) { + if (!byFile.has(tok.file)) byFile.set(tok.file, []) + byFile.get(tok.file).push(tok) + } + for (const [file, list] of byFile) { + console.log(`📄 ${file}:`) + for (const t of list) + console.log(` Line ${t.line}: ${t.name} = ${t.value}`) + console.log() + } + console.log( + '💡 Note: Tokens might be used dynamically or externally and not detected.' + ) + if (profile) console.log(`⏱️ total: ${p.end('total').toFixed(1)}ms`) + return false +} + +if (import.meta.url === `file://${process.argv[1]}`) { + const profile = process.argv.includes('--profile') + validateTokenDefinitions({ profile }) + .then((ok) => process.exit(ok ? 0 : 1)) + .catch((err) => { + console.error('💥 Validation failed:', err?.message || err) + if (err?.stack) console.error(err.stack) + process.exit(1) + }) +} + +export { validateTokenDefinitions } diff --git a/libs/ui/scripts/validate-token-usage.js b/libs/ui/scripts/validate-token-usage.js new file mode 100755 index 0000000000..e9abd1e43d --- /dev/null +++ b/libs/ui/scripts/validate-token-usage.js @@ -0,0 +1,536 @@ +#!/usr/bin/env node + +/** + * Token Usage Validation Script + * + * Validates that all Tailwind classes in components have corresponding token definitions. + * Follows Tailwind v4 theme variable namespace rules for precise mapping. + */ + +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { globSync } from 'glob' + +const ROOT = path.resolve(fileURLToPath(new URL('.', import.meta.url)), '..') + +// Tailwind v4 namespace to utility prefix mappings +const NAMESPACE_MAPPINGS = { + color: [ + 'bg', + 'text', + 'border', + 'fill', + 'stroke', + 'outline', + 'ring', + 'ring-offset', + 'shadow', + 'accent', + 'caret', + 'decoration', + ], + spacing: [ + 'p', + 'px', + 'py', + 'pt', + 'pr', + 'pb', + 'pl', + 'ps', + 'pe', + 'm', + 'mx', + 'my', + 'mt', + 'mr', + 'mb', + 'ml', + 'ms', + 'me', + 'gap', + 'gap-x', + 'gap-y', + 'space-x', + 'space-y', + 'w', + 'h', + 'max-w', + 'min-w', + 'max-h', + 'min-h', + 'top', + 'right', + 'bottom', + 'left', + 'inset', + 'inset-x', + 'inset-y', + ], + text: ['text'], + 'font-weight': ['font'], + font: ['font'], + radius: ['rounded'], + shadow: ['shadow', 'drop-shadow', 'inset-shadow'], + blur: ['blur'], + opacity: ['opacity'], + border: ['border'], +} + +// Standard Tailwind utilities to ignore (not custom tokens) +const IGNORE_PATTERNS = [ + // Standard positioning values + /^(top|right|bottom|left|inset)-(0|px|0\.5|1|1\.5|2|2\.5|3|3\.5|4|5|6|7|8|9|10|11|12|14|16|20|24|28|32|36|40|44|48|52|56|60|64|72|80|96|auto|full|screen|min|max|fit|end)$/, + + // Layout & positioning + /^(flex|grid|block|inline|hidden|absolute|relative|fixed|sticky)$/, + /^(items|justify|content|self)-(start|end|center|stretch|between|around|evenly)$/, + /^(flex|grid)-(row|col|flow|wrap|nowrap)$/, + /^(order|col|row)-(start|end|\d+)$/, + + // Standard spacing (without custom tokens) + /^(p|m|gap|w|h|max-w|min-w|max-h|min-h|top|right|bottom|left|inset|space)-(0|px|0\.5|1|1\.5|2|2\.5|3|3\.5|4|5|6|7|8|9|10|11|12|14|16|20|24|28|32|36|40|44|48|52|56|60|64|72|80|96|auto|full|screen|min|max|fit|end)$/, + + // Margin/padding with directional prefixes + /^(ml|mr|mt|mb|mx|my|pl|pr|pt|pb|px|py)-(0|px|0\.5|1|1\.5|2|2\.5|3|3\.5|4|5|6|7|8|9|10|11|12|14|16|20|24|28|32|36|40|44|48|52|56|60|64|72|80|96|auto|full|screen|min|max|fit)$/, + + // Standard colors including transparent + /^(bg|text|border)-(transparent|current|black|white|slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)(?:-\d{1,3})?(?:\/\d{1,3})?$/, + /^(bg|text|border)-(transparent|current|black|white|inherit)$/, + + // Standard typography + /^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl|left|center|right|justify)$/, + /^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/, + /^font-(sans|serif|mono)$/, + /^(leading|tracking)-(none|tight|snug|normal|relaxed|loose|wide|wider|widest)$/, + + // Standard borders & effects + /^(border|rounded)-(none|sm|md|lg|xl|2xl|3xl|full)$/, + /^border-(0|2|4|8|t|r|b|l|x|y|color)$/, + /^shadow-(sm|md|lg|xl|2xl|inner|none)$/, + /^opacity-(0|5|10|20|25|30|40|50|60|70|75|80|90|95|100)$/, + + // Pseudo-classes and state modifiers + /^(hover|focus|active|disabled|group-hover|group-focus):/, + /^data-\[.+\]:/, + + // Responsive prefixes + /^(sm|md|lg|xl|2xl):/, + + // Transform & animation + /^(transform|rotate|scale|translate|skew|transition|duration|ease|delay|animate)-.+$/, + + // Special edge cases + /^w-\(--reference-width\)$/, // Dynamic width references + /^max-h-\(--available-height\)$/, // Dynamic height references + /^\*:max-h-\(--available-height\)$/, // Selector prefixes + /^left-(1\/2)$/, // Fractional positioning + + // Misc utilities + /^(sr-only|not-sr-only|pointer-events|select|resize|appearance|cursor|outline|ring)-.+$/, +] + +// Precompute prefix helpers for mapping +const KNOWN_PREFIXES = Object.values(NAMESPACE_MAPPINGS) + .flat() + .slice() + .sort((a, b) => b.length - a.length) +const PREFIX_TO_NAMESPACES = (() => { + const map = new Map() + for (const [ns, prefixes] of Object.entries(NAMESPACE_MAPPINGS)) { + for (const p of prefixes) { + if (!map.has(p)) map.set(p, []) + map.get(p).push(ns) + } + } + return map +})() + +const CLASS_STRING_REGEX = /\S+/g + +/** + * Extract Tailwind classes from TypeScript/JSX content + */ +function extractTailwindClasses(content) { + const classes = new Set() + + // Match className props (string values) + const classNameMatches = content.matchAll(/className\s*=\s*["']([^"']+)["']/g) + for (const match of classNameMatches) { + const classString = match[1] + for (const cls of classString.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + + // Match className arrays: className: ['class1', 'class2'] or className={['class1', 'class2']} + const classNameArrayMatches = content.matchAll( + /className\s*[:=]\s*(?:\{)?\s*\[([^\]]+)\]/g + ) + for (const match of classNameArrayMatches) { + const arrayContent = match[1] + // Extract strings from array elements + const stringMatches = arrayContent.matchAll(/['"`]([^'"`]+)['"`]/g) + for (const stringMatch of stringMatches) { + const classString = stringMatch[1] + for (const cls of classString.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + } + + // Match template literals in className + const templateMatches = content.matchAll(/className\s*=\s*`([^`]+)`/g) + for (const match of templateMatches) { + const classString = match[1] + // Extract static classes, ignore interpolations + const staticParts = classString.split(/\$\{[^}]+\}/) + for (const part of staticParts) { + for (const cls of part.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + } + + // Match tailwind-variants tv() configurations + const tvMatches = content.matchAll(/tv\s*\(\s*\{[\s\S]*?\}\s*\)/g) + for (const match of tvMatches) { + const tvConfig = match[0] + + // Extract from slots (arrays) + const slotMatches = tvConfig.matchAll(/\[\s*['"`]([^'"`]+)['"`]/g) + for (const slotMatch of slotMatches) { + const classString = slotMatch[1] + for (const cls of classString.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + + // Extract from variant values + const variantMatches = tvConfig.matchAll( + /:\s*\{\s*[^}]*['"`]([^'"`]+)['"`]/g + ) + for (const variantMatch of variantMatches) { + const classString = variantMatch[1] + for (const cls of classString.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + } + + // Match clsx/cn utility calls + const clsxMatches = content.matchAll(/(?:clsx|cn)\s*\(\s*([^)]+)\)/g) + for (const match of clsxMatches) { + const args = match[1] + const stringMatches = args.matchAll(/['"`]([^'"`]+)['"`]/g) + for (const stringMatch of stringMatches) { + const classString = stringMatch[1] + for (const cls of classString.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + } + + // Match any quoted strings that might be CSS classes (broader approach) + const quotedStringMatches = content.matchAll( + /['"`]([^'"`]*(?:bg-|text-|border-|p-|m-|w-|h-|flex|grid|rounded)[^'"`]*)['"`]/g + ) + for (const match of quotedStringMatches) { + const classString = match[1] + // Only extract if it looks like CSS classes + if (/^[a-z-\s:[\]()]+$/i.test(classString)) { + for (const cls of classString.match(CLASS_STRING_REGEX) || []) { + classes.add(cls.trim()) + } + } + } + + return Array.from(classes).filter((cls) => cls.length > 0) +} + +/** + * Map Tailwind utility class to possible CSS custom properties + */ +function mapClassToPossibleTokens(className) { + // Remove chained state/data prefixes (hover:, focus:, sm:, data-[...]:, etc.) + let baseClass = className + while (/^(?:[a-z-]+:|data-\[[^\]]+\]:)/i.test(baseClass)) { + baseClass = baseClass.replace(/^(?:[a-z-]+:|data-\[[^\]]+\]:)/i, '') + } + + // Parse utility class: prefix-value (non-greedy matching) + // Match known prefixes first, then everything else as value + let prefix = null + let value = null + // Normalize negative utilities (-m-*, -mt-*, etc.) + const normalized = baseClass.startsWith('-') ? baseClass.slice(1) : baseClass + // Try to match against known prefixes (longest first to handle cases like 'ring-offset') + for (const knownPrefix of KNOWN_PREFIXES) { + if (normalized.startsWith(`${knownPrefix}-`)) { + prefix = knownPrefix + value = normalized.slice(knownPrefix.length + 1) // +1 for the dash + break + } + } + + if (!prefix || !value) return [] + + const possibleTokens = [] + + // Find which namespaces this prefix could belong to + const namespaces = PREFIX_TO_NAMESPACES.get(prefix) || [] + for (const namespace of namespaces) { + if (namespace === 'font-weight') { + possibleTokens.push(`--font-weight-${value}`) + } else { + possibleTokens.push(`--${namespace}-${value}`) + } + } + + // Add specific namespace alternatives for spacing-related utilities + if (['p', 'px', 'py', 'pt', 'pr', 'pb', 'pl', 'ps', 'pe'].includes(prefix)) { + possibleTokens.push(`--padding-${value}`) + possibleTokens.push(`--spacing-${value}`) + } + + if ( + [ + 'm', + 'mx', + 'my', + 'mt', + 'mr', + 'mb', + 'ml', + 'ms', + 'me', + '-m', + '-mx', + '-my', + '-mt', + '-mr', + '-mb', + '-ml', + '-ms', + '-me', + ].includes(prefix) + ) { + possibleTokens.push(`--margin-${value}`) + possibleTokens.push(`--spacing-${value}`) + } + + if (['gap', 'gap-x', 'gap-y'].includes(prefix)) { + possibleTokens.push(`--gap-${value}`) + possibleTokens.push(`--spacing-${value}`) + } + + if (['w', 'min-w', 'max-w'].includes(prefix)) { + possibleTokens.push(`--width-${value}`) + } + + if (['h', 'min-h', 'max-h'].includes(prefix)) { + possibleTokens.push(`--height-${value}`) + } + + if (['space-x', 'space-y'].includes(prefix)) { + possibleTokens.push(`--space-${value}`) + possibleTokens.push(`--spacing-${value}`) + } + + if ( + ['inset', 'inset-x', 'inset-y', 'top', 'right', 'bottom', 'left'].includes( + prefix + ) + ) { + possibleTokens.push(`--inset-${value}`) + possibleTokens.push(`--spacing-${value}`) + } + + return possibleTokens +} + +/** + * Load all defined tokens from CSS files + */ +function loadDefinedTokens() { + const tokens = new Set() + const tokenFiles = globSync('src/tokens/**/*.css', { cwd: ROOT }) + + for (const file of tokenFiles) { + const content = fs.readFileSync(path.join(ROOT, file), 'utf8') + // Match CSS custom properties defined in tokens + const tokenMatches = content.matchAll(/--([a-z][a-z0-9-]*)\s*:/g) + for (const match of tokenMatches) { + tokens.add(`--${match[1]}`) + } + } + + // Also treat inline style custom properties in components as defined + const componentFiles = globSync('src/**/*.{ts,tsx}', { + cwd: ROOT, + ignore: ['**/*.stories.tsx', '**/*.test.tsx', '**/*.spec.tsx'], + }) + for (const file of componentFiles) { + const content = fs.readFileSync(path.join(ROOT, file), 'utf8') + // style={{ '--var': value }} or object entries '--var': value + for (const m of content.matchAll(/["'](--[a-z][a-z0-9-]*)["']\s*:/gi)) { + tokens.add(m[1]) + } + } + + return tokens +} + +/** + * Check if a class should be ignored + */ +function shouldIgnoreClass(className) { + // Remove chained state/data prefixes for checking + let baseClass = className + while (/^(?:[a-z-]+:|data-\[[^\]]+\]:)/i.test(baseClass)) { + baseClass = baseClass.replace(/^(?:[a-z-]+:|data-\[[^\]]+\]:)/i, '') + } + return IGNORE_PATTERNS.some((pattern) => pattern.test(baseClass)) +} + +// Extract tokens referenced directly inside arbitrary utility syntax +function extractTokensFromArbitraryUtility(className) { + const tokens = new Set() + // var(--token) + for (const m of className.matchAll(/var\(\s*(--[a-z][a-z0-9-]*)/gi)) { + tokens.add(m[1]) + } + // key:(--token) or key=--token forms inside parentheses/brackets + for (const m of className.matchAll(/[:=]\s*(--[a-z][a-z0-9-]*)/gi)) { + tokens.add(m[1]) + } + // Bare --token immediately after an opening parenthesis. Avoid matching icon names like mdi--play in brackets. + for (const m of className.matchAll(/\((--[a-z][a-z0-9-]*)/gi)) { + tokens.add(m[1]) + } + return Array.from(tokens) +} + +/** + * Main validation function + */ +function validateTokenUsage() { + console.log('🔍 Validating token usage in components...\n') + + const definedTokens = loadDefinedTokens() + console.log(`📋 Found ${definedTokens.size} defined tokens`) + + const componentFiles = globSync('src/**/*.{ts,tsx}', { + cwd: ROOT, + ignore: ['**/*.stories.tsx', '**/*.test.tsx', '**/*.spec.tsx'], + }) + + let totalErrors = 0 + const errorsByFile = new Map() + + for (const file of componentFiles) { + const content = fs.readFileSync(path.join(ROOT, file), 'utf8') + const classes = extractTailwindClasses(content, file) + const fileErrors = [] + + for (const className of classes) { + // 1) Arbitrary utilities with direct token references + const arbitraryTokens = extractTokensFromArbitraryUtility(className) + if (arbitraryTokens.length > 0) { + // Allow some external CSS vars not governed by design tokens + const externalAllow = new Set([ + // Keep truly external/runtime-provided vars here; we now require + // --reference-width and --z-index to exist somewhere, per request. + '--available-height', + '--height', + '--border-width-badge-dynamic', + ]) + const tokensNeedingCheck = arbitraryTokens.filter( + (t) => !externalAllow.has(t) + ) + + const anyDefined = tokensNeedingCheck.some((t) => definedTokens.has(t)) + if (!anyDefined && tokensNeedingCheck.length > 0) { + fileErrors.push({ + className, + expectedTokens: tokensNeedingCheck, + line: + content + .split('\n') + .findIndex((line) => line.includes(className)) + 1, + }) + } + // If tokens are all external or any defined, consider it valid and continue + if (tokensNeedingCheck.length === 0 || anyDefined) continue + } + + // 2) Ignore standard classes + if (shouldIgnoreClass(className)) continue + + // 3) Map to possible tokens via namespace rules + const possibleTokens = mapClassToPossibleTokens(className) + if (possibleTokens.length === 0) continue + + // Check if ANY of the possible tokens exists + const hasMatchingToken = possibleTokens.some((token) => + definedTokens.has(token) + ) + + if (!hasMatchingToken) { + fileErrors.push({ + className, + expectedTokens: possibleTokens, + line: + content.split('\n').findIndex((line) => line.includes(className)) + + 1, + }) + } + } + + if (fileErrors.length > 0) { + errorsByFile.set(file, fileErrors) + totalErrors += fileErrors.length + } + } + + // Report results + if (totalErrors === 0) { + console.log( + '✅ All component classes have corresponding token definitions!' + ) + return true + } + console.log(`❌ Found ${totalErrors} missing token definitions:\n`) + + for (const [file, errors] of errorsByFile) { + console.log(`📄 ${file}:`) + for (const error of errors) { + const tokenList = error.expectedTokens.join(' OR ') + console.log( + ` Line ${error.line}: ${error.className} → Missing token: ${tokenList}` + ) + } + console.log() + } + + return false +} + +/** + * Run validation + */ +if ( + process.argv[1] && + pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url +) { + try { + const success = validateTokenUsage() + process.exit(success ? 0 : 1) + } catch (error) { + console.error('💥 Validation failed:', error.message) + process.exit(1) + } +} + +export { validateTokenUsage, mapClassToPossibleTokens, extractTailwindClasses } diff --git a/libs/ui/src/atoms/badge.tsx b/libs/ui/src/atoms/badge.tsx index 4385546efe..37edc52921 100644 --- a/libs/ui/src/atoms/badge.tsx +++ b/libs/ui/src/atoms/badge.tsx @@ -5,37 +5,37 @@ import { tv } from '../utils' const badgeVariants = tv({ base: [ 'inline-flex items-center justify-center', - 'px-badge-x py-badge-y', - 'rounded-badge border-badge', + 'p-badge', + 'rounded-badge border-badge-border', 'text-badge-size', 'border-(length:--border-width-badge-dynamic)', ], variants: { variant: { primary: [ - 'bg-badge-primary-bg text-badge-primary-fg border-badge-border-primary', + 'bg-badge-bg-primary text-badge-fg-primary border-badge-border-primary', ], secondary: [ - 'bg-badge-secondary-bg text-badge-secondary-fg border-badge-border-secondary', + 'bg-badge-bg-secondary text-badge-fg-secondary border-badge-border-secondary', ], tertiary: [ - 'bg-badge-tertiary-bg text-badge-tertiary-fg border-badge-border-tertiary', + 'bg-badge-bg-tertiary text-badge-fg-tertiary border-badge-border-tertiary', ], discount: [ - 'bg-badge-discount-bg text-badge-discount-fg border-badge-border-discount', + 'bg-badge-bg-discount text-badge-fg-discount border-badge-border-discount', ], - info: ['bg-badge-info-bg text-badge-info-fg border-badge-border-info'], + info: ['bg-badge-bg-info text-badge-fg-info border-badge-border-info'], success: [ - 'bg-badge-success-bg text-badge-success-fg border-badge-border-success', + 'bg-badge-bg-success text-badge-fg-success border-badge-border-success', ], warning: [ - 'bg-badge-warning-bg text-badge-warning-fg border-badge-border-warning', + 'bg-badge-bg-warning text-badge-fg-warning border-badge-border-warning', ], danger: [ - 'bg-badge-danger-bg text-badge-danger-fg border-badge-border-danger', + 'bg-badge-bg-danger text-badge-fg-danger border-badge-border-danger', ], outline: [ - 'bg-badge-outline-bg text-badge-outline-fg border-badge-border-outline', + 'bg-badge-bg-outline text-badge-fg-outline border-badge-border-outline', ], dynamic: [], }, diff --git a/libs/ui/src/atoms/button.tsx b/libs/ui/src/atoms/button.tsx index 7251077796..37a95c3691 100644 --- a/libs/ui/src/atoms/button.tsx +++ b/libs/ui/src/atoms/button.tsx @@ -11,30 +11,30 @@ export const buttonVariants = tv({ 'transition-all duration-200', 'focus:outline-none', 'focus-visible:ring-3 focus-visible:ring-offset-2', - 'disabled:text-btn-text-disabled disabled:pointer-events-none', + 'disabled:text-button-fg-disabled disabled:pointer-events-none', ], variants: { variant: { - primary: 'focus:ring-btn-ring-primary', - secondary: 'focus:ring-btn-ring-secondary', - tertiary: 'focus:ring-btn-ring-tertiary', - danger: 'focus:ring-btn-ring-danger', - warning: 'focus:ring-btn-ring-warning', + primary: 'focus:ring-button-ring-primary', + secondary: 'focus:ring-button-ring-secondary', + tertiary: 'focus:ring-button-ring-tertiary', + danger: 'focus:ring-button-ring-danger', + warning: 'focus:ring-button-ring-warning', }, theme: { - solid: 'disabled:bg-btn-disabled', - light: 'disabled:bg-btn-disabled', + solid: 'disabled:bg-button-bg-disabled', + light: 'disabled:bg-button-bg-disabled', borderless: - 'hover:bg-btn-borderless-hover active:bg-btn-borderless-active', - outlined: 'border disabled:border-btn-border-disabled', + 'bg-button-bg-borderless hover:bg-button-bg-borderless-hover active:bg-button-bg-borderless-active', + outlined: 'border disabled:border-button-border-disabled', }, uppercase: { true: 'uppercase', }, size: { - sm: 'p-btn-sm text-btn-sm rounded-btn-sm gap-btn-sm', - md: 'p-btn-md text-btn-md rounded-btn-md gap-btn-md', - lg: 'p-btn-lg text-btn-lg rounded-btn-lg gap-btn-lg', + sm: 'p-button-sm text-button-sm rounded-button-sm gap-button-sm', + md: 'p-button-md text-button-md rounded-button-md gap-button-md', + lg: 'p-button-lg text-button-lg rounded-button-lg gap-button-lg', }, block: { @@ -46,191 +46,191 @@ export const buttonVariants = tv({ variant: 'primary', theme: 'solid', className: [ - 'bg-btn-primary', - 'hover:bg-btn-primary-hover', - 'active:bg-btn-primary-active', - 'text-btn-text-primary', + 'bg-button-bg-primary', + 'hover:bg-button-bg-primary-hover', + 'active:bg-button-bg-primary-active', + 'text-button-fg-primary', ], }, { variant: 'secondary', theme: 'solid', className: [ - 'bg-btn-secondary', - 'hover:bg-btn-secondary-hover', - 'active:bg-btn-secondary-active', - 'text-btn-text-secondary', + 'bg-button-bg-secondary', + 'hover:bg-button-bg-secondary-hover', + 'active:bg-button-bg-secondary-active', + 'text-button-fg-secondary', ], }, { variant: 'tertiary', theme: 'solid', className: [ - 'bg-btn-tertiary', - 'hover:bg-btn-tertiary-hover', - 'active:bg-btn-tertiary-active', - 'text-btn-text-tertiary', + 'bg-button-bg-tertiary', + 'hover:bg-button-bg-tertiary-hover', + 'active:bg-button-bg-tertiary-active', + 'text-button-fg-tertiary', ], }, { variant: 'warning', theme: 'solid', className: [ - 'bg-btn-warning', - 'hover:bg-btn-warning-hover', - 'active:bg-btn-warning-active', - 'text-btn-text-warning', + 'bg-button-bg-warning', + 'hover:bg-button-bg-warning-hover', + 'active:bg-button-bg-warning-active', + 'text-button-fg-warning', ], }, { variant: 'danger', theme: 'solid', className: [ - 'bg-btn-danger', - 'hover:bg-btn-danger-hover', - 'active:bg-btn-danger-active', - 'text-btn-text-danger', + 'bg-button-bg-danger', + 'hover:bg-button-bg-danger-hover', + 'active:bg-button-bg-danger-active', + 'text-button-fg-danger', ], }, { variant: 'primary', theme: 'light', className: [ - 'bg-btn-primary-light', - 'hover:bg-btn-primary-light-hover', - 'active:bg-btn-primary-light-active', - 'text-btn-text-primary-light', + 'bg-button-bg-primary-light', + 'hover:bg-button-bg-primary-light-hover', + 'active:bg-button-bg-primary-light-active', + 'text-button-fg-primary-light', ], }, { variant: 'secondary', theme: 'light', className: [ - 'bg-btn-secondary-light', - 'hover:bg-btn-secondary-light-hover', - 'active:bg-btn-secondary-light-active', - 'text-btn-text-secondary-light', + 'bg-button-bg-secondary-light', + 'hover:bg-button-bg-secondary-light-hover', + 'active:bg-button-bg-secondary-light-active', + 'text-button-fg-secondary-light', ], }, { variant: 'tertiary', theme: 'light', className: [ - 'bg-btn-tertiary-light', - 'hover:bg-btn-tertiary-light-hover', - 'active:bg-btn-tertiary-light-active', - 'text-btn-text-tertiary-light', + 'bg-button-bg-tertiary-light', + 'hover:bg-button-bg-tertiary-light-hover', + 'active:bg-button-bg-tertiary-light-active', + 'text-button-fg-tertiary-light', ], }, { variant: 'warning', theme: 'light', className: [ - 'bg-btn-warning-light', - 'hover:bg-btn-warning-light-hover', - 'active:bg-btn-warning-light-active', - 'text-btn-text-warning-light', + 'bg-button-bg-warning-light', + 'hover:bg-button-bg-warning-light-hover', + 'active:bg-button-bg-warning-light-active', + 'text-button-fg-warning-light', ], }, { variant: 'danger', theme: 'light', className: [ - 'bg-btn-danger-light', - 'hover:bg-btn-danger-light-hover', - 'active:bg-btn-danger-light-active', - 'text-btn-text-danger-light', + 'bg-button-bg-danger-light', + 'hover:bg-button-bg-danger-light-hover', + 'active:bg-button-bg-danger-light-active', + 'text-button-fg-danger-light', ], }, { variant: 'primary', theme: 'outlined', className: [ - 'border-btn-border-primary', - 'hover:bg-btn-outlined-primary-hover', - 'active:bg-btn-outlined-primary-active', - 'text-btn-text-outlined-primary', + 'border-button-border-primary', + 'hover:bg-button-bg-outlined-primary-hover', + 'active:bg-button-bg-outlined-primary-active', + 'text-button-fg-outlined-primary', ], }, { variant: 'secondary', theme: 'outlined', className: [ - 'border-btn-border-secondary', - 'hover:bg-btn-outlined-secondary-hover', - 'active:bg-btn-outlined-secondary-active', - 'text-btn-text-outlined-secondary', + 'border-button-border-secondary', + 'hover:bg-button-bg-outlined-secondary-hover', + 'active:bg-button-bg-outlined-secondary-active', + 'text-button-fg-outlined-secondary', ], }, { variant: 'tertiary', theme: 'outlined', className: [ - 'border-btn-border-tertiary', - 'hover:bg-btn-outlined-tertiary-hover', - 'active:bg-btn-outlined-tertiary-active', - 'text-btn-text-outlined-tertiary', + 'border-button-border-tertiary', + 'hover:bg-button-bg-outlined-tertiary-hover', + 'active:bg-button-bg-outlined-tertiary-active', + 'text-button-fg-outlined-tertiary', ], }, { variant: 'warning', theme: 'outlined', className: [ - 'border-btn-border-warning', - 'hover:bg-btn-outlined-warning-hover', - 'active:bg-btn-outlined-warning-active', - 'text-btn-text-outlined-warning', + 'border-button-border-warning', + 'hover:bg-button-bg-outlined-warning-hover', + 'active:bg-button-bg-outlined-warning-active', + 'text-button-fg-outlined-warning', ], }, { variant: 'danger', theme: 'outlined', className: [ - 'border-btn-border-danger', - 'hover:bg-btn-outlined-danger-hover', - 'active:bg-btn-outlined-danger-active', - 'text-btn-text-outlined-danger', + 'border-button-border-danger', + 'hover:bg-button-bg-outlined-danger-hover', + 'active:bg-button-bg-outlined-danger-active', + 'text-button-fg-outlined-danger', ], }, { variant: 'primary', theme: 'borderless', - className: ['text-btn-text-primary-borderless'], + className: ['text-button-fg-primary-borderless'], }, { variant: 'secondary', theme: 'borderless', - className: ['text-btn-text-secondary-borderless'], + className: ['text-button-fg-secondary-borderless'], }, { variant: 'tertiary', theme: 'borderless', - className: ['text-btn-text-tertiary-borderless'], + className: ['text-button-fg-tertiary-borderless'], }, { variant: 'warning', theme: 'borderless', - className: ['text-btn-text-warning-borderless'], + className: ['text-button-fg-warning-borderless'], }, { variant: 'danger', theme: 'borderless', - className: ['text-btn-text-danger-borderless'], + className: ['text-button-fg-danger-borderless'], }, { theme: 'outlined', size: 'sm', - className: 'border-(length:--border-btn-width-sm)', + className: 'border-(length:--border-button-width-sm)', }, { theme: 'outlined', size: 'md', - className: 'border-(length:--border-btn-width-md)', + className: 'border-(length:--border-button-width-md)', }, { theme: 'outlined', size: 'lg', - className: 'border-(length:--border-btn-width-lg)', + className: 'border-(length:--border-button-width-lg)', }, ], defaultVariants: { diff --git a/libs/ui/src/atoms/error-text.tsx b/libs/ui/src/atoms/error-text.tsx index c477efb769..52cad84693 100644 --- a/libs/ui/src/atoms/error-text.tsx +++ b/libs/ui/src/atoms/error-text.tsx @@ -4,7 +4,7 @@ import { tv } from '../utils' import { Icon } from './icon' const errorVariants = tv({ - base: ['text-error-text', 'flex items-center gap-1'], + base: ['text-error-fg', 'flex items-center gap-1'], variants: { size: { sm: 'text-error-sm', diff --git a/libs/ui/src/atoms/extra-text.tsx b/libs/ui/src/atoms/extra-text.tsx index b23cff47de..f861c1bfe7 100644 --- a/libs/ui/src/atoms/extra-text.tsx +++ b/libs/ui/src/atoms/extra-text.tsx @@ -3,7 +3,7 @@ import type { VariantProps } from 'tailwind-variants' import { tv } from '../utils' const extraTextVariants = tv({ - base: ['text-helper-text'], + base: ['text-helper-fg'], variants: { size: { sm: 'text-helper-sm', diff --git a/libs/ui/src/atoms/input.tsx b/libs/ui/src/atoms/input.tsx index 0292b3779f..296725cf16 100644 --- a/libs/ui/src/atoms/input.tsx +++ b/libs/ui/src/atoms/input.tsx @@ -6,7 +6,7 @@ const inputVariants = tv({ base: [ 'block w-full', 'bg-input', - 'text-input-text', + 'text-input-fg', 'placeholder:text-input-placeholder', 'border border-input-border', 'rounded-input', @@ -48,8 +48,8 @@ const inputVariants = tv({ }, withButtonInside: { false: '', - right: 'pr-with-button', - left: 'pl-with-button', + right: 'pe-with-button', + left: 'ps-with-button', }, hideSearchClear: { true: '[&::-webkit-search-cancel-button]:hidden [&::-ms-clear]:hidden', @@ -58,7 +58,7 @@ const inputVariants = tv({ true: [ 'bg-input-disabled', 'border-input-border-disabled', - 'text-input-text-disabled', + 'text-input-fg-disabled', 'placeholder:text-input-placeholder-disabled', ], }, diff --git a/libs/ui/src/atoms/label.tsx b/libs/ui/src/atoms/label.tsx index 982b806861..e04915ce6b 100644 --- a/libs/ui/src/atoms/label.tsx +++ b/libs/ui/src/atoms/label.tsx @@ -3,7 +3,7 @@ import type { VariantProps } from 'tailwind-variants' import { tv } from '../utils' const labelVariants = tv({ - base: ['block', 'text-label-text', 'font-label'], + base: ['block', 'text-label-fg', 'font-label'], variants: { size: { sm: 'text-label-sm', @@ -48,7 +48,7 @@ export function Label({ htmlFor={props.htmlFor} > {children} - {required && *} + {required && *} ) } diff --git a/libs/ui/src/atoms/numeric-input.tsx b/libs/ui/src/atoms/numeric-input.tsx index 608580d75a..a15714988b 100644 --- a/libs/ui/src/atoms/numeric-input.tsx +++ b/libs/ui/src/atoms/numeric-input.tsx @@ -25,7 +25,7 @@ const numericInput = tv({ 'flex flex-col h-fit justify-center bg-ni-trigger-container', trigger: [ 'px-ni-trigger-x py-ni-trigger-y', - 'cursor-pointer focus:ring-increment-btn-ring', + 'cursor-pointer focus:ring-increment-btn-ring', ], scrubber: 'absolute inset-0 cursor-ew-resize', }, diff --git a/libs/ui/src/atoms/textarea.tsx b/libs/ui/src/atoms/textarea.tsx index 722433cc66..bdb9141962 100644 --- a/libs/ui/src/atoms/textarea.tsx +++ b/libs/ui/src/atoms/textarea.tsx @@ -6,7 +6,7 @@ const textareaVariants = tv({ base: [ 'block w-full', 'bg-textarea', - 'text-textarea-text', + 'text-textarea-fg', 'placeholder:text-textarea-placeholder', 'border-(length:--textarea-border-width) border-textarea-border', 'rounded-textarea', @@ -14,7 +14,7 @@ const textareaVariants = tv({ 'hover:bg-textarea-hover hover:border-textarea-border-hover', 'focus:outline-none focus:bg-textarea-focus focus:border-textarea-border-focus', 'focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-textarea-ring', - 'disabled:pointer-events-none disabled:bg-textarea-disabled disabled:border-textarea-border-disabled disabled:text-textarea-text-disabled', + 'disabled:pointer-events-none disabled:bg-textarea-disabled disabled:border-textarea-border-disabled disabled:text-textarea-fg-disabled', ], variants: { variant: { @@ -57,7 +57,7 @@ const textareaVariants = tv({ both: 'resize', }, readonly: { - true: 'bg-textarea-disabled cursor-default pointer-events-none opacity-90 text-textarea-text-disabled border-textarea-border-disabled', + true: 'bg-textarea-disabled cursor-default pointer-events-none opacity-90 text-textarea-fg-disabled border-textarea-border-disabled', }, }, defaultVariants: { diff --git a/libs/ui/src/atoms/tooltip.tsx b/libs/ui/src/atoms/tooltip.tsx index 787813b449..df0f0d2dc9 100644 --- a/libs/ui/src/atoms/tooltip.tsx +++ b/libs/ui/src/atoms/tooltip.tsx @@ -5,7 +5,7 @@ import { type VariantProps, tv } from 'tailwind-variants' const tooltipVariants = tv({ slots: { - trigger: ['inline-flex py-0 px-0'], + trigger: ['inline-flex'], content: [ '[--arrow-size:var(--tooltip-arrow-size)]', '[--arrow-background:var(--tooltip-arrow-background)]', @@ -106,14 +106,18 @@ export function Tooltip({ }) const api = tooltip.connect(service as tooltip.Service, normalizeProps) - const { positioner, content: contentSlot } = tooltipVariants({ + const { + trigger, + positioner, + content: contentSlot, + } = tooltipVariants({ size, className, }) return ( <> - + {children} diff --git a/libs/ui/src/molecules/accordion.tsx b/libs/ui/src/molecules/accordion.tsx index 642fdb35fc..a1382507f0 100644 --- a/libs/ui/src/molecules/accordion.tsx +++ b/libs/ui/src/molecules/accordion.tsx @@ -55,7 +55,7 @@ const accordionVariants = tv({ subtitle: 'text-accordion-subtitle-sm', }, md: { - title: 'text-accordion-content-md py-accordion-primary-md', + title: 'text-accordion-title-md py-accordion-primary-md', content: 'text-accordion-content-md p-accordion-secondary-md', subtitle: 'text-accordion-subtitle-md', }, diff --git a/libs/ui/src/molecules/carousel.tsx b/libs/ui/src/molecules/carousel.tsx index 7fd693bb34..fca0df9af6 100644 --- a/libs/ui/src/molecules/carousel.tsx +++ b/libs/ui/src/molecules/carousel.tsx @@ -59,7 +59,7 @@ const carouselVariants = tv({ slots: ['autoplayTrigger', 'indicator', 'prevTrigger', 'nextTrigger'], class: [ 'p-carousel-trigger bg-carousel-trigger-bg', - 'focus:outline-none text-carousel-trigger-text focus:ring-2 focus:ring-carousel-focus-ring focus:ring-offset-2', + 'focus:outline-none text-carousel-trigger-fg focus:ring-2 focus:ring-carousel-focus-ring focus:ring-offset-2', ], }, ], diff --git a/libs/ui/src/molecules/checkbox.tsx b/libs/ui/src/molecules/checkbox.tsx index 4240ae0e1a..a3f808eeae 100644 --- a/libs/ui/src/molecules/checkbox.tsx +++ b/libs/ui/src/molecules/checkbox.tsx @@ -17,7 +17,7 @@ const checkboxVariants = tv({ 'bg-checkbox data-[state=checked]:bg-checkbox-checked border-checkbox', /* center icon, grid and place items do not work e.g. flex */ 'after:absolute after:left-1/2 after:top-1/2 after:-translate-x-1/2 after:-translate-y-1/2', - 'data-[state=checked]:after:token-icon-checkbox text-icon-text data-[state=checked]:text-checkbox-icon-md', + 'data-[state=checked]:after:token-icon-checkbox text-icon-fg data-[state=checked]:text-checkbox-icon-md', 'data-[disabled]:opacity-checkbox-disabled data-[disabled]:cursor-not-allowed data-[disabled]:grayscale', 'transition-all duration-200', 'data-[focus]:outline-none data-[focus]:ring-2 data-[focus]:ring-checkbox-ring data-[focus]:ring-offset-checkbox-offset data-[focus]:ring-offset-2', @@ -25,7 +25,7 @@ const checkboxVariants = tv({ 'data-[invalid]:border-checkbox-border-error', 'data-[state=indeterminate]:bg-checkbox-indeterminate', 'data-[state=indeterminate]:after:w-indeterminate data-[state=indeterminate]:after:h-indeterminate', - 'data-[state=indeterminate]:after:bg-icon-text', + 'data-[state=indeterminate]:after:bg-icon-fg', ], }, }) diff --git a/libs/ui/src/molecules/dialog.tsx b/libs/ui/src/molecules/dialog.tsx index 5023b9b400..eece2249eb 100644 --- a/libs/ui/src/molecules/dialog.tsx +++ b/libs/ui/src/molecules/dialog.tsx @@ -20,7 +20,7 @@ const dialogVariants = tv({ description: ['text-dialog-description-size text-dialog-description-fg'], trigger: [], closeTrigger: [ - 'absolute top-sm right-sm', + 'absolute top-250 right-250', 'flex items-center justify-center', 'p-dialog-close-trigger-padding rounded-dialog-close-trigger', 'text-dialog-close-trigger-fg', diff --git a/libs/ui/src/molecules/form-checkbox.tsx b/libs/ui/src/molecules/form-checkbox.tsx index c1c5385930..824c8857e6 100644 --- a/libs/ui/src/molecules/form-checkbox.tsx +++ b/libs/ui/src/molecules/form-checkbox.tsx @@ -29,7 +29,7 @@ export function FormCheckboxRaw({ const extraTextId = extraText ? `${id}-extra` : undefined return ( -
+
diff --git a/libs/ui/src/molecules/form-input.tsx b/libs/ui/src/molecules/form-input.tsx index 728e095cd3..8c137414e2 100644 --- a/libs/ui/src/molecules/form-input.tsx +++ b/libs/ui/src/molecules/form-input.tsx @@ -39,7 +39,7 @@ export function FormInputRaw({ variant={validateStatus} disabled={disabled} {...props} - className='p-input-sm md:p-input-md' + className="p-input-sm md:p-input-md" /> {/* Status message */} diff --git a/libs/ui/src/molecules/form-numeric-input.tsx b/libs/ui/src/molecules/form-numeric-input.tsx index ae08116648..9b3c4f7c67 100644 --- a/libs/ui/src/molecules/form-numeric-input.tsx +++ b/libs/ui/src/molecules/form-numeric-input.tsx @@ -28,7 +28,7 @@ export function FormNumericInputRaw({ const extraTextId = extraText ? `${id}-extra` : undefined return ( -
+
diff --git a/libs/ui/src/molecules/menu.tsx b/libs/ui/src/molecules/menu.tsx index 7c6616a432..805a3551cc 100644 --- a/libs/ui/src/molecules/menu.tsx +++ b/libs/ui/src/molecules/menu.tsx @@ -65,8 +65,7 @@ const menuVariants = tv({ 'bg-menu-content-bg border border-menu-content-border', 'rounded-menu shadow-menu-content-shadow', 'p-menu-content', - '', - ' overflow-auto', + 'overflow-auto', 'focus:outline-none', 'data-[state=open]:animate-in', 'data-[state=closed]:animate-out', @@ -92,7 +91,7 @@ const menuVariants = tv({ itemText: ['flex-grow'], itemIcon: ['text-menu-item-icon-size text-menu-item-icon-fg'], submenuIndicator: [ - 'ml-menu-submenu-indicator text-menu-submenu-indicator-fg', + 'ms-menu-submenu-indicator text-menu-submenu-indicator-fg', ], }, variants: { @@ -127,6 +126,7 @@ interface SubmenuItemProps { closeOnSelect?: boolean } +// ! TODO: Fix menu.machine typing, it should work without 'as any' function SubmenuItem({ item, parentApi, @@ -142,14 +142,11 @@ function SubmenuItem({ onSelect, }) - const submenuApi = menu.connect( - submenuService as menu.Service, - normalizeProps - ) + const submenuApi = menu.connect(submenuService as any, normalizeProps) useEffect(() => { // Setup parent-child relationship - parentApi.setChild(submenuService as menu.Service) + parentApi.setChild(submenuService as any) submenuApi.setParent(parentService) }, [parentApi, submenuApi, submenuService, parentService]) @@ -177,7 +174,7 @@ function SubmenuItem({ key={menuItem.value} item={menuItem} parentApi={submenuApi} - parentService={submenuService as menu.Service} + parentService={submenuService as any} size={size} onCheckedChange={onCheckedChange} onSelect={onSelect} @@ -347,7 +344,7 @@ export function Menu({ 'aria-label': ariaLabel, }) - const api = menu.connect(service as menu.Service, normalizeProps) + const api = menu.connect(service as any, normalizeProps) const { trigger, @@ -373,7 +370,7 @@ export function Menu({ key={item.value} item={item} parentApi={api} - parentService={service as menu.Service} + parentService={service as any} size={size} onCheckedChange={onCheckedChange} onSelect={onSelect} @@ -436,10 +433,10 @@ export function Menu({ ) : ( diff --git a/libs/ui/src/molecules/product-card.tsx b/libs/ui/src/molecules/product-card.tsx index eed04b59f5..479da4b211 100644 --- a/libs/ui/src/molecules/product-card.tsx +++ b/libs/ui/src/molecules/product-card.tsx @@ -22,11 +22,11 @@ const productCard = tv({ badgesSlot: 'flex flex-wrap gap-pc-box', ratingSlot: 'flex items-center', buttonsSlot: 'flex flex-wrap w-fit', - cartButton: 'bg-btn-cart hover:bg-btn-cart-hover text-btn-cart-fg w-max', + cartButton: 'bg-button-cart hover:bg-button-cart-hover text-button-cart-fg w-max', detailButton: - 'bg-btn-detail hover:bg-btn-detail-hover text-btn-detail-fg w-max', + 'bg-button-detail hover:bg-button-detail-hover text-button-detail-fg w-max', wishlistButton: - 'bg-btn-wishlist hover:bg-btn-wishlist-hover text-btn-wishlist-fg w-max', + 'bg-button-wishlist hover:bg-button-wishlist-hover text-button-wishlist-fg w-max', }, variants: { // variant for layout of the card diff --git a/libs/ui/src/molecules/search-form.tsx b/libs/ui/src/molecules/search-form.tsx index b578e97e7e..6e42a24592 100644 --- a/libs/ui/src/molecules/search-form.tsx +++ b/libs/ui/src/molecules/search-form.tsx @@ -43,7 +43,7 @@ const buttonVariants = tv({ base: [ 'absolute', 'justify-self-end place-self-center', - 'h-full rounded-none px-xs py-xs', + 'h-full rounded-none p-200', ], }) diff --git a/libs/ui/src/molecules/select.tsx b/libs/ui/src/molecules/select.tsx index ceb65e9dae..33c194c769 100644 --- a/libs/ui/src/molecules/select.tsx +++ b/libs/ui/src/molecules/select.tsx @@ -20,7 +20,12 @@ export interface SelectOption { // === COMPONENT VARIANTS === const selectVariants = tv({ slots: { - root: ['relative', 'flex flex-col gap-select-root', 'w-full'], + root: [ + 'relative', + 'flex flex-col gap-select-root', + 'w-full', + 'bg-select-root-bg', + ], control: ['flex relative items-center justify-between', 'w-full'], positioner: ['w-(--reference-width)', 'isolate z-(--z-index)'], trigger: [ @@ -30,6 +35,7 @@ const selectVariants = tv({ 'border border-select-trigger-border rounded-select', 'text-select-trigger text-left', 'hover:bg-select-trigger-hover', + 'focus:bg-select-trigger-focus', 'focus:outline-none', 'data-[disabled]:cursor-not-allowed', 'data-[invalid]:border-select-danger data-[invalid]:ring-select-danger', @@ -45,7 +51,7 @@ const selectVariants = tv({ content: [ 'bg-select-content-bg border border-select-content-border', 'rounded-select shadow-select-content-shadow max-h-fit', - 'h-[calc(var(--available-height)-var(--spacing-lg))]', + 'h-[calc(var(--available-height)-var(--spacing-450))]', 'overflow-auto z-50', ], item: [ diff --git a/libs/ui/src/molecules/steps.tsx b/libs/ui/src/molecules/steps.tsx index f9b18b2143..da31bf17fc 100644 --- a/libs/ui/src/molecules/steps.tsx +++ b/libs/ui/src/molecules/steps.tsx @@ -135,7 +135,11 @@ export function Steps({
{items.map((step, index) => ( -
+
- + diff --git a/libs/ui/stories/molecules/checkbox.stories.tsx b/libs/ui/stories/molecules/checkbox.stories.tsx index 87a25c0522..ab333f870c 100644 --- a/libs/ui/stories/molecules/checkbox.stories.tsx +++ b/libs/ui/stories/molecules/checkbox.stories.tsx @@ -218,7 +218,9 @@ export const IndeterminateTest: Story = { handleChildChange(item.id, checked)} + onCheckedChange={(checked) => + handleChildChange(item.id, checked) + } />
diff --git a/libs/ui/stories/molecules/form-numeric-input.stories.tsx b/libs/ui/stories/molecules/form-numeric-input.stories.tsx index 5998b998f2..9073e640bd 100644 --- a/libs/ui/stories/molecules/form-numeric-input.stories.tsx +++ b/libs/ui/stories/molecules/form-numeric-input.stories.tsx @@ -80,12 +80,37 @@ export const Basic: Story = { return (
- - - + + +
- + setCurrentValue(value)} hideControls={false} validateStatus={currentValue === 10 ? 'error' : 'default'} - helpText={currentValue === 10 ? 'Value cannot be 10' : 'Enter a value other than 10'} + helpText={ + currentValue === 10 + ? 'Value cannot be 10' + : 'Enter a value other than 10' + } />
@@ -116,11 +145,11 @@ export const WithDefaultValues: Story = { hideControls={false} helpText="Value must be between 10 and 20" /> - @@ -244,12 +273,12 @@ export const FormExample: Story = { render: () => { const [quantity, setQuantity] = useState(1) const [price, setPrice] = useState(29.99) - + return ( -
-

Order Form

- + +

Order Form

+ - + - -
-

- Total: {new Intl.NumberFormat('en-US', { + +

+

+ Total:{' '} + {new Intl.NumberFormat('en-US', { style: 'currency', - currency: 'USD' + currency: 'USD', }).format(quantity * price)}

@@ -290,4 +320,4 @@ export const FormExample: Story = { ) }, -} \ No newline at end of file +} diff --git a/libs/ui/stories/molecules/menu.stories.tsx b/libs/ui/stories/molecules/menu.stories.tsx index 6d700931e2..a2f6085a08 100644 --- a/libs/ui/stories/molecules/menu.stories.tsx +++ b/libs/ui/stories/molecules/menu.stories.tsx @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react' -import React from 'react' import { Menu, type MenuItem } from '../../src/molecules/menu' const meta: Meta = { @@ -26,11 +25,26 @@ export default meta type Story = StoryObj const basicItems: MenuItem[] = [ - { type: 'action', value: 'new-file', label: 'New File', icon: 'token-icon-plus' }, - { type: 'action', value: 'open', label: 'Open...', icon: 'token-icon-folder' }, + { + type: 'action', + value: 'new-file', + label: 'New File', + icon: 'token-icon-plus', + }, + { + type: 'action', + value: 'open', + label: 'Open...', + icon: 'token-icon-folder', + }, { type: 'separator', id: 'separator-1' }, { type: 'action', value: 'save', label: 'Save', icon: 'token-icon-save' }, - { type: 'action', value: 'save-as', label: 'Save As...', icon: 'token-icon-save' }, + { + type: 'action', + value: 'save-as', + label: 'Save As...', + icon: 'token-icon-save', + }, { type: 'separator', id: 'separator-2' }, { type: 'action', value: 'exit', label: 'Exit', icon: 'token-icon-close' }, ] @@ -69,11 +83,27 @@ export const WithCustomPositioning: Story = { } const itemsWithDisabled: MenuItem[] = [ - { type: 'action', value: 'cut', label: 'Cut', icon: 'icon-[mdi--content-cut]' }, + { + type: 'action', + value: 'cut', + label: 'Cut', + icon: 'icon-[mdi--content-cut]', + }, { type: 'action', value: 'copy', label: 'Copy', icon: 'token-icon-copy' }, - { type: 'action', value: 'paste', label: 'Paste', icon: 'token-icon-clipboard', disabled: true }, + { + type: 'action', + value: 'paste', + label: 'Paste', + icon: 'token-icon-clipboard', + disabled: true, + }, { type: 'separator', id: 'separator' }, - { type: 'action', value: 'delete', label: 'Delete', icon: 'token-icon-trash' }, + { + type: 'action', + value: 'delete', + label: 'Delete', + icon: 'token-icon-trash', + }, ] export const WithDisabledItems: Story = { @@ -89,7 +119,12 @@ const contextMenuItems: MenuItem[] = [ { type: 'separator', id: 'separator-1' }, { type: 'action', value: 'cut', label: 'Cut', icon: 'token-icon-scissors' }, { type: 'action', value: 'copy', label: 'Copy', icon: 'token-icon-copy' }, - { type: 'action', value: 'paste', label: 'Paste', icon: 'token-icon-clipboard' }, + { + type: 'action', + value: 'paste', + label: 'Paste', + icon: 'token-icon-clipboard', + }, { type: 'separator', id: 'separator-2' }, { type: 'action', value: 'select-all', label: 'Select All' }, ] @@ -105,7 +140,7 @@ export const CustomTrigger: Story = { args: { items: basicItems, customTrigger: ( - ), @@ -117,29 +152,59 @@ export const WithSelectHandler: Story = { items: basicItems, triggerText: 'Actions', onSelect: (details: { value: string }) => { - console.log('Selected:', details.value) alert(`You selected: ${details.value}`) }, }, } const viewMenuItems: MenuItem[] = [ - { type: 'checkbox', value: 'show-sidebar', label: 'Show Sidebar', checked: true }, - { type: 'checkbox', value: 'show-toolbar', label: 'Show Toolbar', checked: true }, - { type: 'checkbox', value: 'show-statusbar', label: 'Show Status Bar', checked: false }, + { + type: 'checkbox', + value: 'show-sidebar', + label: 'Show Sidebar', + checked: true, + }, + { + type: 'checkbox', + value: 'show-toolbar', + label: 'Show Toolbar', + checked: true, + }, + { + type: 'checkbox', + value: 'show-statusbar', + label: 'Show Status Bar', + checked: false, + }, { type: 'separator', id: 'separator-1' }, - { type: 'radio', value: 'list-view', label: 'List View', name: 'view-mode', checked: true }, - { type: 'radio', value: 'grid-view', label: 'Grid View', name: 'view-mode', checked: false }, - { type: 'radio', value: 'detail-view', label: 'Detail View', name: 'view-mode', checked: false }, + { + type: 'radio', + value: 'list-view', + label: 'List View', + name: 'view-mode', + checked: true, + }, + { + type: 'radio', + value: 'grid-view', + label: 'Grid View', + name: 'view-mode', + checked: false, + }, + { + type: 'radio', + value: 'detail-view', + label: 'Detail View', + name: 'view-mode', + checked: false, + }, ] export const WithOptionsMenu: Story = { args: { items: viewMenuItems, triggerText: 'View', - onCheckedChange: (item: MenuItem, checked: boolean) => { - console.log('Option changed:', (item as any).value, 'checked:', checked) - }, + onCheckedChange: (_item: MenuItem, _checked: boolean) => {}, }, } @@ -153,7 +218,8 @@ export const KeyboardNavigation: Story = { parameters: { docs: { description: { - story: 'Use arrow keys to navigate, Enter to select, and type to search', + story: + 'Use arrow keys to navigate, Enter to select, and type to search', }, }, }, @@ -170,27 +236,81 @@ const positioningItems: MenuItem[] = [ export const DifferentPlacements: Story = { render: () => (
- - - - + + + +
), } // Story pro mixed content const mixedContentItems: MenuItem[] = [ - { type: 'action', value: 'profile', label: 'My Profile', icon: 'token-icon-user' }, - { type: 'action', value: 'settings', label: 'Settings', icon: 'token-icon-settings' }, + { + type: 'action', + value: 'profile', + label: 'My Profile', + icon: 'token-icon-user', + }, + { + type: 'action', + value: 'settings', + label: 'Settings', + icon: 'token-icon-settings', + }, { type: 'separator', id: 'sep-1' }, - { type: 'checkbox', value: 'notifications', label: 'Enable Notifications', checked: true }, + { + type: 'checkbox', + value: 'notifications', + label: 'Enable Notifications', + checked: true, + }, { type: 'checkbox', value: 'sounds', label: 'Enable Sounds', checked: false }, { type: 'separator', id: 'sep-2' }, - { type: 'radio', value: 'light', label: 'Light Theme', name: 'theme', checked: true }, - { type: 'radio', value: 'dark', label: 'Dark Theme', name: 'theme', checked: false }, - { type: 'radio', value: 'system', label: 'System Theme', name: 'theme', checked: false }, + { + type: 'radio', + value: 'light', + label: 'Light Theme', + name: 'theme', + checked: true, + }, + { + type: 'radio', + value: 'dark', + label: 'Dark Theme', + name: 'theme', + checked: false, + }, + { + type: 'radio', + value: 'system', + label: 'System Theme', + name: 'theme', + checked: false, + }, { type: 'separator', id: 'sep-3' }, - { type: 'action', value: 'logout', label: 'Logout', icon: 'token-icon-logout' }, + { + type: 'action', + value: 'logout', + label: 'Logout', + icon: 'token-icon-logout', + }, ] export const ComplexMenu: Story = { @@ -225,8 +345,18 @@ export const ScrollableMenu: Story = { // Story pro nested menu const nestedMenuItems: MenuItem[] = [ - { type: 'action', value: 'new-file', label: 'New File', icon: 'token-icon-plus' }, - { type: 'action', value: 'open', label: 'Open...', icon: 'token-icon-folder' }, + { + type: 'action', + value: 'new-file', + label: 'New File', + icon: 'token-icon-plus', + }, + { + type: 'action', + value: 'open', + label: 'Open...', + icon: 'token-icon-folder', + }, { type: 'separator', id: 'sep-1' }, { type: 'submenu', @@ -248,8 +378,18 @@ const nestedMenuItems: MenuItem[] = [ label: 'Share', icon: 'token-icon-share', items: [ - { type: 'action', value: 'email', label: 'Email', icon: 'token-icon-email' }, - { type: 'action', value: 'link', label: 'Copy Link', icon: 'token-icon-link' }, + { + type: 'action', + value: 'email', + label: 'Email', + icon: 'token-icon-email', + }, + { + type: 'action', + value: 'link', + label: 'Copy Link', + icon: 'token-icon-link', + }, { type: 'separator', id: 'sep-share' }, { type: 'submenu', @@ -264,7 +404,12 @@ const nestedMenuItems: MenuItem[] = [ }, ], }, - { type: 'action', value: 'print', label: 'Print...', icon: 'token-icon-print' }, + { + type: 'action', + value: 'print', + label: 'Print...', + icon: 'token-icon-print', + }, { type: 'separator', id: 'sep-3' }, { type: 'action', value: 'exit', label: 'Exit', icon: 'token-icon-close' }, ] @@ -273,14 +418,13 @@ export const NestedMenu: Story = { args: { items: nestedMenuItems, triggerText: 'File', - onSelect: (details: { value: string }) => { - console.log('Selected from nested menu:', details.value) - }, + onSelect: (_details: { value: string }) => {}, }, parameters: { docs: { description: { - story: 'Menu with nested submenus. Supports multiple levels of nesting.', + story: + 'Menu with nested submenus. Supports multiple levels of nesting.', }, }, }, @@ -382,14 +526,14 @@ export const CategoryMenu: Story = { triggerText: 'Shop by Category', triggerIcon: 'token-icon-grid', onSelect: (details: { value: string }) => { - console.log('Selected category:', details.value) alert(`Navigate to category: ${details.value}`) }, }, parameters: { docs: { description: { - story: 'Complex nested menu structure suitable for e-commerce categories with multiple levels of hierarchy.', + story: + 'Complex nested menu structure suitable for e-commerce categories with multiple levels of hierarchy.', }, }, }, @@ -397,7 +541,12 @@ export const CategoryMenu: Story = { // Story pro mixed nested menu with options const mixedNestedItems: MenuItem[] = [ - { type: 'action', value: 'dashboard', label: 'Dashboard', icon: 'token-icon-home' }, + { + type: 'action', + value: 'dashboard', + label: 'Dashboard', + icon: 'token-icon-home', + }, { type: 'separator', id: 'sep-1' }, { type: 'submenu', @@ -405,17 +554,45 @@ const mixedNestedItems: MenuItem[] = [ label: 'View', icon: 'token-icon-eye', items: [ - { type: 'checkbox', value: 'show-sidebar', label: 'Show Sidebar', checked: true }, - { type: 'checkbox', value: 'show-toolbar', label: 'Show Toolbar', checked: true }, + { + type: 'checkbox', + value: 'show-sidebar', + label: 'Show Sidebar', + checked: true, + }, + { + type: 'checkbox', + value: 'show-toolbar', + label: 'Show Toolbar', + checked: true, + }, { type: 'separator', id: 'sep-view' }, { type: 'submenu', value: 'theme', label: 'Theme', items: [ - { type: 'radio', value: 'light', label: 'Light', name: 'theme', checked: true }, - { type: 'radio', value: 'dark', label: 'Dark', name: 'theme', checked: false }, - { type: 'radio', value: 'system', label: 'System', name: 'theme', checked: false }, + { + type: 'radio', + value: 'light', + label: 'Light', + name: 'theme', + checked: true, + }, + { + type: 'radio', + value: 'dark', + label: 'Dark', + name: 'theme', + checked: false, + }, + { + type: 'radio', + value: 'system', + label: 'System', + name: 'theme', + checked: false, + }, ], }, { @@ -423,9 +600,27 @@ const mixedNestedItems: MenuItem[] = [ value: 'layout', label: 'Layout', items: [ - { type: 'radio', value: 'comfortable', label: 'Comfortable', name: 'layout', checked: true }, - { type: 'radio', value: 'compact', label: 'Compact', name: 'layout', checked: false }, - { type: 'radio', value: 'spacious', label: 'Spacious', name: 'layout', checked: false }, + { + type: 'radio', + value: 'comfortable', + label: 'Comfortable', + name: 'layout', + checked: true, + }, + { + type: 'radio', + value: 'compact', + label: 'Compact', + name: 'layout', + checked: false, + }, + { + type: 'radio', + value: 'spacious', + label: 'Spacious', + name: 'layout', + checked: false, + }, ], }, ], @@ -436,10 +631,25 @@ const mixedNestedItems: MenuItem[] = [ label: 'Tools', icon: 'token-icon-settings', items: [ - { type: 'action', value: 'import', label: 'Import Data...', icon: 'token-icon-download' }, - { type: 'action', value: 'export', label: 'Export Data...', icon: 'token-icon-upload' }, + { + type: 'action', + value: 'import', + label: 'Import Data...', + icon: 'token-icon-download', + }, + { + type: 'action', + value: 'export', + label: 'Export Data...', + icon: 'token-icon-upload', + }, { type: 'separator', id: 'sep-tools' }, - { type: 'action', value: 'preferences', label: 'Preferences...', icon: 'token-icon-settings' }, + { + type: 'action', + value: 'preferences', + label: 'Preferences...', + icon: 'token-icon-settings', + }, ], }, { type: 'separator', id: 'sep-2' }, @@ -450,18 +660,15 @@ export const MixedNestedMenu: Story = { args: { items: mixedNestedItems, triggerText: 'Application', - onSelect: (details: { value: string }) => { - console.log('Action selected:', details.value) - }, - onCheckedChange: (item: MenuItem, checked: boolean) => { - console.log('Option changed:', (item as any).value, 'checked:', checked) - }, + onSelect: (_details: { value: string }) => {}, + onCheckedChange: (_item: MenuItem, _checked: boolean) => {}, }, parameters: { docs: { description: { - story: 'Nested menu with mixed content types including checkboxes and radio buttons in submenus.', + story: + 'Nested menu with mixed content types including checkboxes and radio buttons in submenus.', }, }, }, -} \ No newline at end of file +} diff --git a/libs/ui/stories/molecules/popover.stories.tsx b/libs/ui/stories/molecules/popover.stories.tsx index cf199a7763..443e1c6f28 100644 --- a/libs/ui/stories/molecules/popover.stories.tsx +++ b/libs/ui/stories/molecules/popover.stories.tsx @@ -1,10 +1,10 @@ import type { Meta, StoryObj } from '@storybook/react' import { useState } from 'react' -import { Popover } from '../../src/molecules/popover' import { Button } from '../../src/atoms/button' +import { Icon } from '../../src/atoms/icon' import { Input } from '../../src/atoms/input' import { Label } from '../../src/atoms/label' -import { Icon } from '../../src/atoms/icon' +import { Popover } from '../../src/molecules/popover' const meta = { title: 'Molecules/Popover', @@ -81,7 +81,7 @@ export const DifferentSizes: Story = {

Small content

- + Medium content

- +
Top End
- +
Left
@@ -131,7 +131,7 @@ export const DifferentPlacements: Story = {
Right
- +
Bottom Start
@@ -148,7 +148,7 @@ export const DifferentPlacements: Story = { export const Controlled: Story = { render: () => { const [open, setOpen] = useState(false) - + return (
@@ -159,7 +159,7 @@ export const Controlled: Story = { Close Popover
- +

The popover is {open ? 'open' : 'closed'}.

- +
), @@ -222,11 +235,11 @@ export const CustomTrigger: Story = {

This popover uses a custom trigger with an icon.

- + -

Click this custom area

+
+

Click this custom area

} triggerClassName="hover:border-primary focus:border-primary" @@ -256,13 +269,16 @@ export const Modal: Story = { trigger: 'Open Modal Popover', modal: true, title: 'Modal Popover', - description: 'This popover acts as a modal - it traps focus and blocks interactions outside.', + description: + 'This popover acts as a modal - it traps focus and blocks interactions outside.', showCloseButton: true, closeOnInteractOutside: false, children: (

Try clicking outside - it won't close!

-

Press Escape or use the close button to dismiss.

+

+ Press Escape or use the close button to dismiss. +

), }, @@ -272,18 +288,18 @@ export const AsyncContent: Story = { render: () => { const [loading, setLoading] = useState(false) const [data, setData] = useState(null) - + const loadData = async () => { setLoading(true) setData(null) - + // Simulate API call - await new Promise(resolve => setTimeout(resolve, 1500)) - + await new Promise((resolve) => setTimeout(resolve, 1500)) + setData('Data loaded successfully!') setLoading(false) } - + return ( -
+
{loading ? (
- + Loading...
) : data ? (
- +

{data}

) : ( @@ -315,14 +340,10 @@ export const AsyncContent: Story = { export const NestedPopovers: Story = { render: () => ( - +

This is the first level popover.

- +

This is nested inside the first popover.

- + +

This popover contains a lot of content that might need scrolling.

- + {Array.from({ length: 10 }, (_, i) => ( -
+

Section {i + 1}

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.

))} - -

End of content

+ +

End of content

), }, @@ -381,10 +402,10 @@ export const NonPortalled: Story = { children: (

This popover is rendered in place, not in a portal.

-

+

Useful when you need the popover to be clipped by a parent container.

), }, -} \ No newline at end of file +} diff --git a/libs/ui/stories/molecules/steps.stories.tsx b/libs/ui/stories/molecules/steps.stories.tsx index 97e60c2c3f..6e70610ef3 100644 --- a/libs/ui/stories/molecules/steps.stories.tsx +++ b/libs/ui/stories/molecules/steps.stories.tsx @@ -1,8 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react' import { useState } from 'react' -import { Steps } from '../../src/molecules/steps' import { Select } from '../../src/molecules/select' -import { Button } from '../../src/atoms/button' +import { Steps } from '../../src/molecules/steps' const meta: Meta = { title: 'Molecules/Steps', @@ -133,10 +132,10 @@ const stepsWithCustomContent = [ value: 0, title: 'Account', content: ( -
-

Create your account

+
+

Create your account

This is custom content for the account step.

-
@@ -151,11 +150,18 @@ const stepsWithCustomContent = [ value: 2, title: 'Settings', content: ( -
+

Custom Settings Form

-
), diff --git a/libs/ui/stories/molecules/toast.stories.tsx b/libs/ui/stories/molecules/toast.stories.tsx index ef649283da..db7402096d 100644 --- a/libs/ui/stories/molecules/toast.stories.tsx +++ b/libs/ui/stories/molecules/toast.stories.tsx @@ -288,19 +288,19 @@ export const PauseResumeExample: Story = { {toastId && ( + size="sm" + variant="secondary" + onClick={() => { + if (isPaused) { + toaster.resume(toastId) + } else { + toaster.pause(toastId) + } + setIsPaused(!isPaused) + }} + > + {isPaused ? 'Resume Timer' : 'Pause Timer'} + )}
diff --git a/libs/ui/stories/molecules/tree-view.stories.tsx b/libs/ui/stories/molecules/tree-view.stories.tsx index 7c73e8b65d..418779b62d 100644 --- a/libs/ui/stories/molecules/tree-view.stories.tsx +++ b/libs/ui/stories/molecules/tree-view.stories.tsx @@ -457,8 +457,7 @@ export const TypeaheadDebugDemo: Story = { { id: 'beta', name: 'Beta' }, { id: 'gamma', name: 'Gamma' }, ] - const handleKeyDown = (_e: React.KeyboardEvent) => { - } + const handleKeyDown = (_e: React.KeyboardEvent) => {} return (
diff --git a/libs/ui/stories/overview/color-palette.stories.tsx b/libs/ui/stories/overview/color-palette.stories.tsx new file mode 100644 index 0000000000..257d4e0359 --- /dev/null +++ b/libs/ui/stories/overview/color-palette.stories.tsx @@ -0,0 +1,105 @@ +import type { Meta, StoryObj } from '@storybook/react' +import '../../src/tokens/_colors.css' +import '../../src/tokens/_semantic.css' +import { ColorSelect } from '../../../../apps/frontend-demo/src/components/atoms/color-select' + +const meta: Meta = { + title: 'Overview/Color Palette', + parameters: { + layout: 'fullscreen', + }, +} + +export default meta +type Story = StoryObj + +const semanticColors = [ + 'primary', + 'secondary', + 'tertiary', + 'info', + 'success', + 'warning', + 'danger', +] as const + +const colorVariants = [ + { suffix: '', label: 'Default' }, + { suffix: '-light', label: 'Light' }, +] as const + +const stateVariants = [ + { state: '', label: 'Default' }, + { state: 'hover', label: 'Hover' }, + { state: 'active', label: 'Active' }, + { state: 'disabled', label: 'Disabled' }, +] as const + +export const Default: Story = { + render: () => ( +
+
+
+ {semanticColors.map((color) => ( +
+

+ {color} +

+ +
+ {colorVariants.map(({ suffix, label }) => ( +
+

+ {label} +

+
+ {stateVariants.map(({ state, label: stateLabel }) => { + const baseColorVar = `--color-${color}${suffix}` + const computedColor = + state === 'disabled' + ? `var(--color-${color}${suffix}-disabled)` + : state !== '' + ? `oklch(from var(${baseColorVar}) calc(l + var(--state-${state})) c h)` + : `var(${baseColorVar})` + + const colorTokenName = + state === 'disabled' + ? `--color-${color}${suffix}-disabled` + : state !== '' + ? `--color-${color}${suffix}-${state}` + : `--color-${color}${suffix}` + + return ( +
+
+ +
+
+
+ {state || 'Default'} +
+
+ {colorTokenName} +
+
+
+ ) + })} +
+
+ ))} +
+
+ ))} +
+
+
+ ), +} diff --git a/libs/ui/stories/templates/comprehensive-form.stories.tsx b/libs/ui/stories/templates/comprehensive-form.stories.tsx index 98b255cde2..e171d28694 100644 --- a/libs/ui/stories/templates/comprehensive-form.stories.tsx +++ b/libs/ui/stories/templates/comprehensive-form.stories.tsx @@ -4,11 +4,12 @@ import { VariantContainer, VariantGroup } from '../../.storybook/decorator' import { ErrorText } from '../../src/atoms/error-text' import { ExtraText } from '../../src/atoms/extra-text' import { Label } from '../../src/atoms/label' +import { NumericInput } from '../../src/atoms/numeric-input' import { Textarea } from '../../src/atoms/textarea' import { Combobox } from '../../src/molecules/combobox' import { FormCheckbox } from '../../src/molecules/form-checkbox' import { FormInput } from '../../src/molecules/form-input' -import { NumericInput } from '../../src/atoms/numeric-input' +import { RangeSlider } from '../../src/molecules/range-slider' import { Select } from '../../src/molecules/select' import { Switch } from '../../src/molecules/switch' diff --git a/libs/ui/token-contribution.md b/libs/ui/token-contribution.md new file mode 100644 index 0000000000..a6a0d86253 --- /dev/null +++ b/libs/ui/token-contribution.md @@ -0,0 +1,354 @@ +## 🚀 QUICK REFERENCE + +### Essential Rules +1. **All colors need suffixes**: `-bg`, `-fg`, `-border` (never just `--color-button`) +2. **Two-layer strategy**: Reference layer → Derived tokens +3. **No abbreviations**: `button` not `btn`, `product-card` not `pc` +4. **Follow the pattern**: `--[prefix]-[component]-[part?]-[property]-[state?]` + +### Validation +```bash +pnpm validate:tokens # Check token naming compliance +pnpm check:unused-tokens # Find unused tokens +``` + +### Real Example (Button) +```css +/* Optional: only if needed */ +:root { + --opacity-outlined-hover: 16%; +} + +@theme static { + /* Reference layer first */ + --color-button-primary: var(--color-primary); + --color-button-secondary: var(--color-secondary); + + /* Then derived tokens */ + --color-button-bg-primary: var(--color-button-primary); + --color-button-fg-primary: var(--color-fg-reverse); + + /* Spacing - separate single and composite */ + --spacing-button-sm: var(--spacing-150); + --padding-button-sm: var(--spacing-150) var(--spacing-250); +} +``` + +--- + +🎯 BASIC PRINCIPLES + +1. Predictability Over Brevity + +- Token names MUST be self-explanatory +- No abbreviations that are not widely known +- Consistency is more important than short names + +2. Single Source of Truth + +- Each component has a reference layer for theming +- All derived tokens use references +- One token = one responsibility + +3. Scalable Architecture + +- The pattern must work for 100+ components +- Must support variants, states, themes +- Automated validation rules + +--- +📋 NAMING CONVENTION AUTHORITY + +Mandatory Pattern + +--[prefix]-[component]-[part?]-[property]-[state?] + +Required Prefixes + +| PREFIX | USAGE | EXAMPLE | +|------------|------------------------------------------------|---------------------------| +| --color- | ALL colors | --color-button-bg-primary | +| --spacing- | Spacing including width/height needing max/min | --spacing-button-padding | +| --text- | Font sizes only | --text-button-lg | +| --border- | Border properties | --border-button-width | +| --radius- | Border radius | --radius-button-sm | +| --shadow- | Box shadows | --shadow-button-focus | +| --opacity- | Transparency values | --opacity-button-disabled | + +Required Suffixes for Colors + +| SUFFIX | USAGE | FORBIDDEN | +|-----------|------------------------|---------------------------| +| -bg | Background colors | --color-button ❌ | +| -fg | Foreground/text colors | --color-button-text ❌ | +| -border | Border colors | --color-button-outline ❌ | +| -hover | Hover states | --color-button-hovered ❌ | +| -active | Active states | --color-button-pressed ❌ | +| -disabled | Disabled states | --color-button-inactive ❌ | +| -focus | Focus states | --color-button-focused ❌ | + +Component Naming Rules + +/* ✅ CORRECT */ +--color-button-bg-primary +--color-accordion-fg-title +--color-product-card-bg + +/* ❌ FORBIDDEN */ +--color-btn-primary /* Use 'button' not 'btn' */ +--color-pc-bg /* Use 'product-card' not 'pc' */ +--color-button /* Missing -bg suffix */ +--color-button-text /* Use -fg not -text */ + +--- +🏗️ MANDATORY FILE STRUCTURE + +/* === COMPONENT TOKEN FILE STRUCTURE === */ + +/* 1. LOCAL VARIABLES (OPTIONAL) */ +/* Only when needed for opacity values or complex calculations */ +:root { + --opacity-outlined-hover: 16%; + --opacity-outlined-active: 12%; +} + +/* 2. THEME TOKENS */ +@theme static { + /* === BASE COLOR MAPPING === */ + /* Reference layer - single source of truth for theming */ + + /* === DERIVED COLORS === */ + /* Background, foreground, border colors using references */ + + /* === STATE VARIATIONS === */ + /* Auto-calculated from derived colors */ + + /* === COMPONENT VARIANTS === */ + /* Light, outlined, borderless variants */ + + /* === VALIDATION STATES === */ + /* Success, warning, danger states */ + + /* === DISABLED STATES === */ + /* Disabled appearance */ + + /* === SPACING === */ + /* Padding, margin, gap values */ + + /* === TYPOGRAPHY === */ + /* Font sizes and weights */ + + /* === BORDERS & RADIUS === */ + /* Border widths and radius values */ + + /* === SHADOWS === */ + /* Box shadow definitions */ + + /* === FOCUS RINGS === */ + /* Focus ring colors and styles */ +} + +/* 3. UTILITIES */ +/* Component-specific utility classes */ +@utility component-variant { + /* utility definitions */ +} + +/* 4. SEMANTIC ICON TOKENS */ +/* Icon mappings if needed */ +@utility token-icon-component-action { + @apply icon-[mdi--icon-name]; +} + +--- +🎨 COLOR TOKEN ARCHITECTURE + +Reference Layer Pattern + +/* === BASE COLOR MAPPING === */ +/* Reference layer - single source of truth for theming */ +--color-component-primary: var(--color-primary); /* Theme reference */ +--color-component-secondary: var(--color-secondary); /* Theme reference */ +--color-component-base: var(--color-surface); /* Base reference */ +--color-component-success: var(--color-success); /* State reference */ +--color-component-warning: var(--color-warning); /* State reference */ +--color-component-danger: var(--color-danger); /* State reference */ + +Derived Colors + +/* === DERIVED COLORS === */ +/* Background colors - using reference layer */ +--color-button-bg-primary: var(--color-button-primary); +--color-button-bg-secondary: var(--color-button-secondary); +--color-button-bg-danger: var(--color-button-danger); + +/* Foreground colors - can have shared base */ +--color-button-fg: var(--color-fg-reverse); /* Shared base */ +--color-button-fg-primary: var(--color-button-fg); +--color-button-fg-secondary: var(--color-button-fg); +--color-button-fg-danger: var(--color-button-fg); + +/* Border colors */ +--color-button-border-primary: var(--color-button-primary); +--color-button-border-secondary: var(--color-button-secondary); + +/* Aliases for special cases */ +--color-badge-bg-discount: var(--color-badge-danger); /* Alias */ + +State Calculations + +/* === STATE VARIATIONS === */ +/* Auto-calculated using semantic state modifiers */ +--color-component-bg-hover: oklch( + from var(--color-component-bg) calc(l + var(--state-hover)) c h +); +--color-component-bg-primary-hover: oklch( + from var(--color-component-bg-primary) calc(l + var(--state-hover)) c h +); +--color-component-bg-primary-active: oklch( + from var(--color-component-bg-primary) calc(l + var(--state-active)) c h +); + +--- +📏 SPACING & SIZING TOKENS + +Spacing Patterns + +/* === SPACING === */ +/* Single spacing values for uniform padding */ +--spacing-button-sm: var(--spacing-150); /* Small size */ +--spacing-button-md: var(--spacing-200); /* Medium size */ +--spacing-button-lg: var(--spacing-250); /* Large size */ + +/* Composite padding (vertical horizontal) */ +--padding-button-sm: var(--spacing-150) var(--spacing-250); +--padding-button-md: var(--spacing-200) var(--spacing-350); +--padding-button-lg: var(--spacing-250) var(--spacing-450); + +/* Simple composite for badges */ +--padding-badge: var(--spacing-100) var(--spacing-100); + +Typography Tokens + +/* === TYPOGRAPHY === */ +--text-component-sm: var(--text-sm); /* Small text */ +--text-component-md: var(--text-md); /* Medium text */ +--text-component-lg: var(--text-lg); /* Large text */ +--font-weight-component: var(--font-weight-medium); /* Font weight */ +--font-weight-component-title: var(--font-weight-semibold); /* Title weight */ + +--- +💡 VARIANT PATTERNS + +Component Variants + +/* === LIGHT VARIANTS === */ +--color-component-bg-primary-light: var(--color-primary-light); +--color-component-fg-primary-light: var(--color-fg-reverse); +--color-component-bg-primary-light-hover: oklch( + from var(--color-component-bg-primary-light) calc(l + var(--state-hover)) c h +); + +/* === OUTLINED VARIANTS === */ +--color-component-bg-outlined-primary: transparent; +--color-component-fg-outlined-primary: var(--color-component-primary); +--color-component-border-outlined-primary: var(--color-component-primary); +--color-component-bg-outlined-primary-hover: --alpha( + var(--color-component-primary) / var(--opacity-outlined-hover) +); + +/* === BORDERLESS VARIANTS === */ +--color-component-bg-borderless: transparent; +--color-component-fg-borderless: var(--color-fg-primary); +--color-component-border-borderless: transparent; +--color-component-bg-borderless-hover: var(--color-fill-hover); + +--- +🔧 COMPONENT INTEGRATION + +Using Tokens in Components + +```typescript +// With tailwind-variants (tv) - Real button example +const buttonVariants = tv({ + base: 'transition-colors font-medium', + variants: { + variant: { + primary: 'bg-button-bg-primary text-button-fg-primary hover:bg-button-bg-primary-hover', + secondary: 'bg-button-bg-secondary text-button-fg-secondary hover:bg-button-bg-secondary-hover', + outlined: 'bg-transparent border-button-border-primary text-button-fg-outlined-primary' + }, + size: { + sm: 'p-button-sm text-button-sm rounded-button-sm', + md: 'p-button-md text-button-md rounded-button-md', + lg: 'p-button-lg text-button-lg rounded-button-lg' + } + } +}) +``` + +State-Based Styling + +```typescript +// Use data attributes for dynamic states +'data-[state=open]:bg-button-bg-open' +'data-[highlighted]:bg-button-bg-hover' +'data-[disabled]:opacity-button-disabled' +'data-[validation=error]:border-button-border-danger' +``` + +--- +🚨 VALIDATION RULES + +Mandatory Checks + +1. All colors MUST have explicit suffix (-bg, -fg, -border) +2. No abbreviations except universally known (no btn, pc, etc.) +3. Reference layer before derived colors +4. Consistent section ordering +5. No hardcoded values in component tokens (use semantic references) + +Forbidden Patterns + +/* ❌ FORBIDDEN */ +--color-button: /* Missing suffix */ +--color-btn-primary: /* Abbreviation */ +--color-button-text: /* Use -fg not -text */ +--color-button-primary: #3b82f6; /* Hardcoded value */ +--padding-btn-sm: /* Use 'button' not 'btn' */ +--spacing-component: 1rem; /* Use semantic token */ + +/* ✅ CORRECT */ +--color-button-bg-primary: /* Explicit suffix */ +--color-button-primary: var(--color-primary); /* Reference layer */ +--color-button-fg-primary: /* Use -fg for text */ +--spacing-button-padding: var(--spacing-200); /* Semantic reference */ + +--- +📝 COMMENT CONVENTIONS + +Section Headers + +/* === SECTION NAME === */ + +Subsection Headers + +/* Subsection description */ + +Inline Comments + +--token-name: value; /* Explanation when needed */ + +Required Section Order + +1. /* === BASE COLOR MAPPING === */ +2. /* === DERIVED COLORS === */ +3. /* === STATE VARIATIONS === */ +4. /* === COMPONENT VARIANTS === */ +5. /* === VALIDATION STATES === */ +6. /* === DISABLED STATES === */ +7. /* === SPACING === */ +8. /* === TYPOGRAPHY === */ +9. /* === BORDERS & RADIUS === */ +10. /* === SHADOWS === */ +11. /* === FOCUS RINGS === */ \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df7acbc2d5..17690c92b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -79,11 +79,11 @@ importers: specifier: ^0.7.1 version: 0.7.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@zag-js/carousel': - specifier: ^1.14.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/react': - specifier: ^1.14.0 - version: 1.15.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^1.22.1 + version: 1.22.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -579,68 +579,68 @@ importers: libs/ui: dependencies: '@zag-js/accordion': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/carousel': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/checkbox': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/combobox': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/dialog': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/i18n-utils': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/menu': - specifier: ^1.15.0 - version: 1.18.2 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/number-input': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/pagination': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/popover': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/rating-group': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/react': - specifier: ^1.15.0 - version: 1.15.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^1.22.1 + version: 1.22.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@zag-js/select': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/slider': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/steps': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/switch': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/tabs': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/toast': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/tooltip': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 '@zag-js/tree-view': - specifier: ^1.15.0 - version: 1.15.0 + specifier: ^1.22.1 + version: 1.22.1 tailwind-variants: - specifier: ^1.0.0 - version: 1.0.0(tailwindcss@4.1.8) + specifier: ^3.1.0 + version: 3.1.0(tailwind-merge@3.3.0)(tailwindcss@4.1.8) devDependencies: '@eslint/js': specifier: ^9.0.0 @@ -703,11 +703,14 @@ importers: specifier: ^9.0.0 version: 9.26.0(jiti@2.4.2) eslint-plugin-tailwindcss: - specifier: npm:@hyoban/eslint-plugin-tailwindcss@4.0.0-alpha.12 - version: '@hyoban/eslint-plugin-tailwindcss@4.0.0-alpha.12(tailwindcss@4.1.8)' + specifier: beta + version: 4.0.0-beta.0(tailwindcss@4.1.8) glob: specifier: ^11.0.2 version: 11.0.3 + next: + specifier: ^15.5.0 + version: 15.5.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.84.0) react: specifier: ^19.1.0 version: 19.1.0 @@ -1829,6 +1832,9 @@ packages: '@emnapi/runtime@1.4.3': resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/wasi-threads@1.0.2': resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} @@ -2302,12 +2308,18 @@ packages: '@floating-ui/core@1.7.2': resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + '@floating-ui/dom@1.7.1': resolution: {integrity: sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==} '@floating-ui/dom@1.7.2': resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/react-dom@2.1.2': resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} peerDependencies: @@ -2448,12 +2460,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@hyoban/eslint-plugin-tailwindcss@4.0.0-alpha.12': - resolution: {integrity: sha512-IKPK7PgoHoo9TEQLccNXLUXdlLxFHNrcr++NIEGtKMNfae99ygUdtLOPsq23zy6V7Xh3njzr/urADwdWYsql4Q==} - engines: {node: '>=18.12.0'} - peerDependencies: - tailwindcss: ^3.4.0 || ^4.0.0 - '@iconify/json@2.2.338': resolution: {integrity: sha512-wKUTEGEQwkgbY+P84c2njj/D9iEXoKr/Gv6Abwnw+tuElkT57JsGfMFAUpZpaKDL5fTD/+noRRA4L10mFOtIYw==} @@ -2474,110 +2480,232 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-darwin-arm64@0.34.3': + resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.1': resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + '@img/sharp-darwin-x64@0.34.3': + resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.1.0': resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.2.0': + resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.1.0': resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.0': + resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-linux-arm64@1.1.0': resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linux-arm64@1.2.0': + resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linux-arm@1.1.0': resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} cpu: [arm] os: [linux] + '@img/sharp-libvips-linux-arm@1.2.0': + resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + cpu: [arm] + os: [linux] + '@img/sharp-libvips-linux-ppc64@1.1.0': resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} cpu: [ppc64] os: [linux] + '@img/sharp-libvips-linux-ppc64@1.2.0': + resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + cpu: [ppc64] + os: [linux] + '@img/sharp-libvips-linux-s390x@1.1.0': resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} cpu: [s390x] os: [linux] + '@img/sharp-libvips-linux-s390x@1.2.0': + resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + cpu: [s390x] + os: [linux] + '@img/sharp-libvips-linux-x64@1.1.0': resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} cpu: [x64] os: [linux] + '@img/sharp-libvips-linux-x64@1.2.0': + resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + cpu: [x64] + os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.1.0': resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} cpu: [x64] os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + cpu: [x64] + os: [linux] + '@img/sharp-linux-arm64@0.34.1': resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linux-arm64@0.34.3': + resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linux-arm@0.34.1': resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + '@img/sharp-linux-arm@0.34.3': + resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.3': + resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + '@img/sharp-linux-s390x@0.34.1': resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + '@img/sharp-linux-s390x@0.34.3': + resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + '@img/sharp-linux-x64@0.34.1': resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-linux-x64@0.34.3': + resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.1': resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.3': + resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linuxmusl-x64@0.34.1': resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-linuxmusl-x64@0.34.3': + resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-wasm32@0.34.1': resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-wasm32@0.34.3': + resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.3': + resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + '@img/sharp-win32-ia32@0.34.1': resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] + '@img/sharp-win32-ia32@0.34.3': + resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-x64@0.34.1': resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] + '@img/sharp-win32-x64@0.34.3': + resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@inquirer/checkbox@2.5.0': resolution: {integrity: sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==} engines: {node: '>=18'} @@ -2611,8 +2739,8 @@ packages: '@internationalized/number@3.6.1': resolution: {integrity: sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==} - '@internationalized/number@3.6.2': - resolution: {integrity: sha512-E5QTOlMg9wo5OrKdHD6edo1JJlIoOsylh0+mbf0evi1tHJwMZfJSaBpGtnJV9N7w3jeiioox9EG/EWRWPh82vg==} + '@internationalized/number@3.6.4': + resolution: {integrity: sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==} '@internationalized/string@3.2.6': resolution: {integrity: sha512-LR2lnM4urJta5/wYJVV7m8qk5DrMZmLRTuFhbQO5b9/sKLHgty6unQy1Li4+Su2DWydmB4aZdS5uxBRXIq2aAw==} @@ -3525,6 +3653,9 @@ packages: '@next/env@15.3.2': resolution: {integrity: sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==} + '@next/env@15.5.2': + resolution: {integrity: sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==} + '@next/eslint-plugin-next@15.2.1': resolution: {integrity: sha512-6ppeToFd02z38SllzWxayLxjjNfzvc7Wm07gQOKSLjyASvKcXjNStZrLXMHuaWkhjqxe+cnhb2uzfWXm1VEj/Q==} @@ -3543,6 +3674,12 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@15.5.2': + resolution: {integrity: sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@15.3.0': resolution: {integrity: sha512-m+eO21yg80En8HJ5c49AOQpFDq+nP51nu88ZOMCorvw3g//8g1JSUsEiPSiFpJo1KCTQ+jm9H0hwXK49H/RmXg==} engines: {node: '>= 10'} @@ -3555,6 +3692,12 @@ packages: cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@15.5.2': + resolution: {integrity: sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@15.3.0': resolution: {integrity: sha512-H0Kk04ZNzb6Aq/G6e0un4B3HekPnyy6D+eUBYPJv9Abx8KDYgNMWzKt4Qhj57HXV3sTTjsfc1Trc1SxuhQB+Tg==} engines: {node: '>= 10'} @@ -3567,6 +3710,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-gnu@15.5.2': + resolution: {integrity: sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@15.3.0': resolution: {integrity: sha512-k8GVkdMrh/+J9uIv/GpnHakzgDQhrprJ/FbGQvwWmstaeFG06nnAoZCJV+wO/bb603iKV1BXt4gHG+s2buJqZA==} engines: {node: '>= 10'} @@ -3579,6 +3728,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.5.2': + resolution: {integrity: sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-x64-gnu@15.3.0': resolution: {integrity: sha512-ZMQ9yzDEts/vkpFLRAqfYO1wSpIJGlQNK9gZ09PgyjBJUmg8F/bb8fw2EXKgEaHbCc4gmqMpDfh+T07qUphp9A==} engines: {node: '>= 10'} @@ -3591,6 +3746,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-gnu@15.5.2': + resolution: {integrity: sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@15.3.0': resolution: {integrity: sha512-RFwq5VKYTw9TMr4T3e5HRP6T4RiAzfDJ6XsxH8j/ZeYq2aLsBqCkFzwMI0FmnSsLaUbOb46Uov0VvN3UciHX5A==} engines: {node: '>= 10'} @@ -3603,6 +3764,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.5.2': + resolution: {integrity: sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-win32-arm64-msvc@15.3.0': resolution: {integrity: sha512-a7kUbqa/k09xPjfCl0RSVAvEjAkYBYxUzSVAzk2ptXiNEL+4bDBo9wNC43G/osLA/EOGzG4CuNRFnQyIHfkRgQ==} engines: {node: '>= 10'} @@ -3615,6 +3782,12 @@ packages: cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@15.5.2': + resolution: {integrity: sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-x64-msvc@15.3.0': resolution: {integrity: sha512-vHUQS4YVGJPmpjn7r5lEZuMhK5UQBNBRSB+iGDvJjaNk649pTIcRluDWNb9siunyLLiu/LDPHfvxBtNamyuLTw==} engines: {node: '>= 10'} @@ -3627,6 +3800,12 @@ packages: cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.5.2': + resolution: {integrity: sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -7279,140 +7458,116 @@ packages: resolution: {integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==} engines: {node: '>=18.12.0'} - '@zag-js/accordion@1.15.0': - resolution: {integrity: sha512-EKNeuKx+lOQ/deCe/ApCjVPxpxpDwT2NXvMPL+YvqXmSv7hAnTLs9fDKjbDUQUMmsyx32BsBd8t6d17DL3rPXg==} - - '@zag-js/anatomy@1.15.0': - resolution: {integrity: sha512-r0l5I7mSsF35HdwXm22TppNhfVftFuqvKfHvTUw+wQZhni4eUL93HypJD0Fl7mDhtP5zfVGfBwR048OzD0+tCw==} + '@zag-js/accordion@1.22.1': + resolution: {integrity: sha512-P3jsauxnAGKBhuqs9gdivjEiSu7N7KnKRlgWlIpyti35askz8swHsqxsfkc2ASs9tcPKnPvuZDHIxXmJmZSLuQ==} - '@zag-js/anatomy@1.18.2': - resolution: {integrity: sha512-GxwOUfSDrnwU4oROohKBy0TRKPlYjD0dhuFHo52ZJLSPDkr8H8DlE/y3rFlb6BaGVO/bHjCUeJlaZzZgIpFK0g==} + '@zag-js/anatomy@1.22.1': + resolution: {integrity: sha512-I5OvOuJBt6hEqbpqVkWCOEoDfGMnKuLx+S0h7Un5SyAwnif3F1dSqDYujU28bCy8FtKs36vsq/izxufXyiXSEg==} - '@zag-js/aria-hidden@1.15.0': - resolution: {integrity: sha512-3ogglAasycekTHI34ph16mqwM+VtHCOMtrFHWzPwB16itV5oDEeeMNdQXenHSSyQ/07nJ2QsRGFFjGhPm1kWNg==} + '@zag-js/aria-hidden@1.22.1': + resolution: {integrity: sha512-vPfAE35BfYPS1UbYRcNw8/kMl7uayE7LyRncK/gPMnoQMjmEKW0nXmD5WlCHFLdGX9WFGYTIde8k4U8ay+oqcg==} - '@zag-js/carousel@1.15.0': - resolution: {integrity: sha512-ZI9H34f2utdJ2Ek6GZa+iuRH4eC99GHD/VEOKLdGani8uadpT2v8M5kUwPGrlAJq9SiPbQ2UuXBmCkmurPQqdA==} + '@zag-js/carousel@1.22.1': + resolution: {integrity: sha512-bFbCRe5xarBtD3NnozHmCmrGJ+nLRhqLQFq+RG13fl1hlhUJaJ5AsS7e8L1r2ZLdbVVrsB0lUuW/ocfJ/G4MSw==} - '@zag-js/checkbox@1.15.0': - resolution: {integrity: sha512-6lQvPQNJXt7R0xxdpOuh2qtmAkzdBdqSvFIH7fE6GJzJ/AWiRZh0X+9deLQ76CN4EDUdxizEe7MlQfTI3a56aw==} + '@zag-js/checkbox@1.22.1': + resolution: {integrity: sha512-A/cZb89Aeb2k/KGl3ITS2fuLBXwq6Rnq9aFirfKs/UHrY16fopRbRjfqOxF6wm8lWoFk3gqmRGgybo8qsIfxog==} - '@zag-js/collection@1.15.0': - resolution: {integrity: sha512-oC3i6c/oP/FuNPsfgoC1reSXbAvDBGXl0HU3CcvXiNLHbjg2ek8J7kbow6MNuXK6chiksiOHbzKxHl2Oo0Ox7A==} + '@zag-js/collection@1.22.1': + resolution: {integrity: sha512-jjeSKALTH3iK2vTI6uAh2NCtS9n+e2r1cGERKCfNkbt86U6VSp9xiXqalUsEI4ovNIPcgg0+/nzixoVwFO1Vgg==} - '@zag-js/combobox@1.15.0': - resolution: {integrity: sha512-HBck3wcEeIOa7IQMsUkUKbm9cAU7bjoklIyq2zFGn90k7DcDa++oXK9Z2pmcd4TPoBYiyVuuXucaCcjmLX8V/Q==} + '@zag-js/combobox@1.22.1': + resolution: {integrity: sha512-N4tGTmezfHGaKB0+aDB5yMuVzBv2ShgsAx1uizom6ElcvlYD2rsQTr3xLc4wyOR7fx0z6fFDo1+63/Dt3y0t4A==} - '@zag-js/core@1.15.0': - resolution: {integrity: sha512-P/8F3IXabMhpFnc6hC7GDg3rvUnvY27cuZU04hxjUqTH6+SfORIA/Uvqd4ekhC+dIprL9jicnFrmGgcyelyxfQ==} + '@zag-js/core@1.22.1': + resolution: {integrity: sha512-4BNrwO9Tadq2Z0d2xSSQs4O/o3OarEHzXM2FQqx46vrwSE57qUghnZex429ZQ51fuk8AL5Lowt26a9JxE9sVPg==} - '@zag-js/core@1.18.2': - resolution: {integrity: sha512-feKLPL8OMJIegwiGwQwoKI4iB9vA/Gf4d5IOZ+KH0X/5S4lCJ3dswmki+Jtu2Ce2PiyYc7oClvG3CM5mfywtfQ==} + '@zag-js/dialog@1.22.1': + resolution: {integrity: sha512-b5KwMPYKc9RenZwxrAAHu6aHPz7tqPy4Mxa/YR5zo1pXBV4amA7u2xnqyncRaK65Z7y5QKmpmDuBp+0PnXxNIA==} - '@zag-js/dialog@1.15.0': - resolution: {integrity: sha512-Vlt5vySs4u8c8xBEh2JMUvRfPc+aaVEIIUtFVxpc2ORWhBXs9glijyp1yf3rNHJhjj8gqqhF5sEvs3yUTTAk+Q==} + '@zag-js/dismissable@1.22.1': + resolution: {integrity: sha512-0DzbykJu9QoXYw4Zcjte69Mtk6ThNRCXWxxCKBf930V8Bw3Ha7vfY5bgdb4RFT5K+BQP3E8vLT+PzIaDINn2Xw==} - '@zag-js/dismissable@1.15.0': - resolution: {integrity: sha512-yv575KWy8gA1p4aajOiY5l/nBQ3Xw+Mrjpungp1+wiGd/98eNAIKJ6/adldfbE1Ygd/Q4Dx2VQ7D1AmiTdwUSw==} + '@zag-js/dom-query@1.22.1': + resolution: {integrity: sha512-mtvGj2z3rkl40mkjd+QwoOHvxqpiOkY4mtVjzNzgzcbVtUN63Mz7giW8OZB+KLy37hwFX0B8JfiQncU8IOHNpw==} - '@zag-js/dismissable@1.18.2': - resolution: {integrity: sha512-uv4FE62TuxWR/wSdr3wfQ9GRW2EHJYt4/HvhVH+mFno2JVRwm9/rSHDUc6QILabXrDVfnp/PdkPJ1rtsIzoOGA==} + '@zag-js/focus-trap@1.22.1': + resolution: {integrity: sha512-6W9cG0LEVICt0srVfWSpamKzsnRxXMdl3gV+GQ5HvkCCk1Sw6Io4tc3QvSSvaWcfyhM07feerOsa2ah7qiT/ig==} - '@zag-js/dom-query@1.15.0': - resolution: {integrity: sha512-z8H/j/Zs0eZEsGpbonScmlKSv0jEXKiAwUCrvQ9Mt6Gz9n0CQRM3MkFclSsM8aeiSv6qKLlhPfkzjl18OLkbgA==} + '@zag-js/focus-visible@1.22.1': + resolution: {integrity: sha512-TuBEux3UTivo9VXPPe79q9JfTwaP/uIshL1KPifg51ofGYesWjMGeE5S5MAuaSzUmH9+3CpnwP7h7f65s3D0kw==} - '@zag-js/dom-query@1.18.2': - resolution: {integrity: sha512-/yUfu4u527vL32mDYwoziEWfLLWfIBenwBo/v8JcDVJwrtBw/1OEPFU7lK9iDa7BAKaIBAGhY0pwsiFLT5UxzA==} + '@zag-js/i18n-utils@1.22.1': + resolution: {integrity: sha512-45KUYB9tu1br6NmgtaNW9NviozYCYUxJ8aZTI/Y6vKotXK/Pn3bIlaiOaq4Zel7TalGYT8gVnwgPe2E6H5sqTg==} - '@zag-js/focus-trap@1.15.0': - resolution: {integrity: sha512-N8m/JpNe1gHUPJlr0hyGUdHg6pAuyJKkBaX0s38cyVntlo2CJhyAWZGuUdocpT2Q3HNPql666FNnH986rYPDKQ==} + '@zag-js/interact-outside@1.22.1': + resolution: {integrity: sha512-+iZ3xHC9+jVo2FCC4B9c9ntcXv19shVOqQGDr2cD30Hwmwtm9kCOdVydMqv3Lp3UhR8a105MXEVUAKg53WbCoA==} - '@zag-js/focus-visible@1.15.0': - resolution: {integrity: sha512-TPXBf47tj6L0hhZNl9AWhuLoVzfPaNPM+/Gw8t9l9Whvy6v9rk/rqUCidY5LsrQuPiKTi7s5WI5J+Wod8ib3gw==} + '@zag-js/menu@1.22.1': + resolution: {integrity: sha512-a5pgQgcpVTVyY6JM8k1WGqelHVKSPwV2CwOv2oGjHWXIr2fpRCAKqZRtytE5PvUP/CZArk8bCjatmgOWe1RdPQ==} - '@zag-js/i18n-utils@1.15.0': - resolution: {integrity: sha512-anxSbT8kLbJaFJFSb0Ork2j/Lp+XVfMNCIgiBR2BuqUlfX72k23TIJvRxAfwNIkUfs0L8ikaSgLss9OwS4mAnw==} + '@zag-js/number-input@1.22.1': + resolution: {integrity: sha512-E4DROYvSo5TFJMkSmnq+f75wSTL/N7SK6MR8ssNlA2oQp69iVWXhIlFLe4knekX02QJzK1MF97aVU332kAYTeQ==} - '@zag-js/interact-outside@1.15.0': - resolution: {integrity: sha512-OwBf/iesQGU9Oq3xe/tcK7gu7xipiGWsmwl2CcScr0fTp3BIMbQywHS928IgPk1DxA8KTHodY8wBjoY1dskfRA==} + '@zag-js/pagination@1.22.1': + resolution: {integrity: sha512-Jeix+sXcfMPm5jer2W4PHSUCgu9a11aC/AOBk6dkxbX8XL23fYXJu5YyOVVq0iQIDWzX4Uij1N/vBha64ARmcA==} - '@zag-js/interact-outside@1.18.2': - resolution: {integrity: sha512-X2S3h/+MM5I83EnWihR2eHJYd1xbqfWeCO+Lz05V6+aWmJHpRPrniHGoVKyKocpSqmgQPrUMqY9ONmVuq4EPRA==} + '@zag-js/popover@1.22.1': + resolution: {integrity: sha512-27VVkhaEOtiHJYj2j++AzYlAzpMcW0ED05TV9wIT1q0EYzASWxweSBajbnCiQf9TIYzCImDiNVDaCMl5D+TamQ==} - '@zag-js/menu@1.18.2': - resolution: {integrity: sha512-jLxJY0Zg7ogMEJbo6VSDWWbxLGgU0LO1onhP3qqgl/aK1P+Rg5uZC5cq4e2OOjMz1DdMc6WLyjEvcD5pIt8gxQ==} + '@zag-js/popper@1.22.1': + resolution: {integrity: sha512-vBI5WpvE/3ugsimjZaNisOwcECiYfzc+3LIJwaU8od62kInZ1XF6m096BvV7JGwP0FjkMPJrgjcv7weDtY2iDQ==} - '@zag-js/number-input@1.15.0': - resolution: {integrity: sha512-+kK8kyXJhIAbEUnswoMDR+DSJUmvDNIOW0ffuZ9pbfukN3p6zaA3/dCp2Dtg3bQS7hGrFWgtrdejJ8l+mVvUAA==} + '@zag-js/rating-group@1.22.1': + resolution: {integrity: sha512-QxBK+hpfkQ4yFHUr1YOSwEQ3LuTrdS32J9zV8UyHu8HbgwzfR7L8ZAa1PUUmG65tupzua2pbn1NioOkMvDmBOQ==} - '@zag-js/pagination@1.15.0': - resolution: {integrity: sha512-Z62Q41fQPWqk59QyJk+9J0Ad3H9DCqZ0zZutI6iH8DdzT0A0xxmT6zhup6DM/8C8h0OLlaHFTWQnj0RdRNrnXg==} - - '@zag-js/popover@1.15.0': - resolution: {integrity: sha512-cdzEed3zcGbjSgPQnQnrsuXo2hVVslmSNwQbU5dHcNzG1uxxmtPCIMVeBUmGyJbAFF5XQpKCq/7mIr26dT73vw==} - - '@zag-js/popper@1.15.0': - resolution: {integrity: sha512-Ra/0Ko423KN+8D4+mIFFkeTn9uaHfpxn6UUNIWwZKoiJQvED8DH4dPbLbmvGEoKp6qmisnRHAzi71NLgEhk0Mw==} - - '@zag-js/popper@1.18.2': - resolution: {integrity: sha512-RAhYpUhDyEjW40787d2FGcPHga8Z6t3vUmNLcoAS5FhIPlZcyPNmUo6PEPndo3Or8ZKrTNFzrU4qOQZ3Jvbf0w==} - - '@zag-js/rating-group@1.15.0': - resolution: {integrity: sha512-omGKN97FhplFwBX9J/Mj7BCZuwFXSXssSVTKU7Yp2d1Cmxhez4+Ju7KdSRNnIoWB4OxFCxwZyaAPTcg3E0Pjrg==} - - '@zag-js/react@1.15.0': - resolution: {integrity: sha512-YSp9QBkdeBfZt4nVhJW+CUd5sNEEVAuwkmoZWDFUoDoWSAXwzSKuHCmTm5/8DaXg1IZD2bMrXgMNDqZv2x0hZw==} + '@zag-js/react@1.22.1': + resolution: {integrity: sha512-TcIKkNo9EFel+d92nb7104voKJNDiMkqq9nn7Ozq/TE8A62JPf5zk8y8zqoxTbGDTTk+tDjW7Sm1IKb4r6rX4w==} peerDependencies: react: '>=18.0.0' react-dom: '>=18.0.0' - '@zag-js/rect-utils@1.18.2': - resolution: {integrity: sha512-g26aaea2vWOG9Ahg1Y8HSRmOwMbgWjStvIo+KtP9ToKwoulQ7+xB3x13O3eDyGed2YBLnFjBj8lZB5Epwq39iQ==} + '@zag-js/rect-utils@1.22.1': + resolution: {integrity: sha512-jtI03SR9kF0AcBffoFI/TKXn5KyhjNCtsGlqbWw0dKbhWTNy1v432FDC5opmmnH8W5LjjWebIzo4QtO5+632QQ==} - '@zag-js/remove-scroll@1.15.0': - resolution: {integrity: sha512-vdWSAdgY8wJ7s4YeaKwTMwmZiRMBxCehmdktSxBWvwtAjU1cM3UWvjmZ9E6INJrQXxH9vDpe/rpFSyv1guIQIw==} + '@zag-js/remove-scroll@1.22.1': + resolution: {integrity: sha512-2TrS8ljp8SADX5xRB/+KGBCBYbYTeH0k5IEalG2rt8ReNyNAW1JfCrm53KCVoCg9YmxKF3MrxPgPT83MNFsJhQ==} - '@zag-js/scroll-snap@1.15.0': - resolution: {integrity: sha512-/LfBlsjoR4tVL3Djus3k9jKLhwC2ApdHTACxEc72TAewoPe4M8icnSDLXmKHvwwOhzK0HlFz8wGm6ZncAbQbuA==} + '@zag-js/scroll-snap@1.22.1': + resolution: {integrity: sha512-kctqJiteALaavoHEpYBDSPgUErIdwAoY5jcrU4Mq5L8FjtI4tSNr8BWcXzSBK2UVqaKN+vDo+PDcj7XIXTUQJA==} - '@zag-js/select@1.15.0': - resolution: {integrity: sha512-4urUBADzhrsGEO/UsqHdjsgmDdF15Zzeid3ejEbIMTrkt2/mMMcQ1CShuxtsWqm2EUBz/N1kOcZlE6Tq69n7Xg==} + '@zag-js/select@1.22.1': + resolution: {integrity: sha512-sWq0RqlJvmj0heJDpfS3OfM1ynSSCW+fYY5v3T/QyH4qneqB8OJjgh8EEBaHlOkbqv/oBsk855U8/o6jegfUxw==} - '@zag-js/slider@1.15.0': - resolution: {integrity: sha512-NYIsn3GKXIoPmvkDXsQmw9wdYg3QHbYHXnZ8Ewl2fVubN7S5mDlHSZs2iDVsBvX+a4RChWFRO6JHX8E1+BncOg==} + '@zag-js/slider@1.22.1': + resolution: {integrity: sha512-aricrX99r21RAS9TyPNTJL8gE8mNRSQMy7TIXTa9aoeRjN0Cf6+PSksKfmPdP9l249/nplGqvC25Ck7XUVJn6A==} - '@zag-js/steps@1.15.0': - resolution: {integrity: sha512-VoIDcDIEErZawmW2m0yTGlffqjfRuSwR37K9LdSRy8Q4Qzz3wV7jASaTjMhTya1hlreJ7tJg+Qbjqowvw9GndA==} + '@zag-js/steps@1.22.1': + resolution: {integrity: sha512-eJCHbHG9aGAbzb/IQCqpmk6fmwSmIfocAxNKVTljroD6OHkBtqgaZQVS3q4xyjz61nB/d/0ZlsvpCVjm1EhwBw==} - '@zag-js/store@1.15.0': - resolution: {integrity: sha512-ecqjcy3b1GsULpsT8RVJV9KDaikajRN0XRg48HMvaGkaPIvxI6esyrE6RKnShuqr2eVXIPghgBnCnrJUev4UlA==} + '@zag-js/store@1.22.1': + resolution: {integrity: sha512-KrMWi/Fa4cqOjx2zDSMIu6vztFYik+V3K6VPWRVONM4FkboLpTqAEayzwgTTNqMK9iYYZIYjhiPhAVLW9iLuBg==} - '@zag-js/switch@1.15.0': - resolution: {integrity: sha512-2CaAUTi7jM4lJjCYoSE1HWlFPCifI5GR+hufWOCYKpanf8VA/LM+t/a2Aq5QoBsWdcQv3B9mHxF/aVTDbnCKPQ==} + '@zag-js/switch@1.22.1': + resolution: {integrity: sha512-ipmBHEqtcrPYr5WS5Juj5dt4GFIqr81NYVNe8RHMW8jIHgHhRCRj3TokGXVlZ7HdseCKTTNNrcvRFBr1sJBbOw==} - '@zag-js/tabs@1.15.0': - resolution: {integrity: sha512-voHWpibC1TKLmbAJfixOesxrCio7wK+gdLRvh7Xh5u+3VSsT2fP2wEw3ySkJbpw3MpEE7R2OWkInbCV/SwPcsA==} + '@zag-js/tabs@1.22.1': + resolution: {integrity: sha512-B0WHW36uuR+pu/24X0yI4eyvSwo7WmqOc5C3ohZHOf03zkmMJdtMtVQSotKr7qhGMt5updCgs68MR7jAmmc1Lw==} - '@zag-js/toast@1.15.0': - resolution: {integrity: sha512-0RupMCXyGr7/La4Zlei7VqBF0VPNJelGd7zimLboe+IKZyy4Ypi/N2IX14rl8JZQDsDEgkLUl33xrSk/9RW2nQ==} + '@zag-js/toast@1.22.1': + resolution: {integrity: sha512-cxcfbMftA//ggOAlxG3q04WZVL/mMVklvtQ2rSyj3oRmnwocJPYXtJzKIRazWBjji3u3BOA+ZeOI1AcGrfp/TQ==} - '@zag-js/tooltip@1.15.0': - resolution: {integrity: sha512-sOpVECyfdS4RZBx46mSV+RPc9C5k9JvYQYUfoOVWh0E5RLSEz5bQm5xxctKOHfCOv+vJNTfG5gP596B1r2+Fkw==} + '@zag-js/tooltip@1.22.1': + resolution: {integrity: sha512-0ub0p22CzYnaXv0prAnWNjqUBkdw4nO4yGk5qntaodajpLNQ4gSdq7Hj4afHzJqwbKAkwb3KzJFqcqIm9Y/dfw==} - '@zag-js/tree-view@1.15.0': - resolution: {integrity: sha512-wqdd+hu1bDOCWtnZ8MarRFHqbZF2t8qKBM3kO42IBq7jTI/93LCkHSlceEPft9dgZ6Ea9km0YJMHhoTqCPZ/fw==} + '@zag-js/tree-view@1.22.1': + resolution: {integrity: sha512-AQmOn1mB+nLJEaq0xdSVnTI8Vt3nB3OweqdB12jkbdIOcWI9eY0RfhiNHC0k0mgAw+dMjyn84op/gOd9VVdtmA==} - '@zag-js/types@1.15.0': - resolution: {integrity: sha512-lV2ov2M07BlmjDUCSwBeHxPApHI3oAiLytG94AqcYvQ0BtsCRo5T60yRQ0syFc6fHf0e9+kwt89uoIgfGFYfmw==} + '@zag-js/types@1.22.1': + resolution: {integrity: sha512-lvpDSMR96e7H7TdwOiVpMzj6css5Ydix1nBi7BlmjME6v5OPR0KZwVDGD6h5UtTeVjPq8dPaqM8TJWw+QwbQSw==} - '@zag-js/types@1.18.2': - resolution: {integrity: sha512-iyKwrhRLbMs+y22j8PdqdW7waIo98jbneNI4MmXOVbQUe2AgSfDnapL/JuO58hJ7vshdYrmkcoMzehwNSkYKXw==} - - '@zag-js/utils@1.15.0': - resolution: {integrity: sha512-XctFny5H8C00BsougV40Yp0qVEj9M2d/NRme7B33mon9wG+3hscZwP6miJmF6BYI5Pgu6e2P0Sv45FddQU1Tkg==} - - '@zag-js/utils@1.18.2': - resolution: {integrity: sha512-tGrG2Qnm5qf95VJEBHunrEDHO0OJZGU81FoZU2VNC+YkRmD4C1phcvyxVLklDFT569rNxIHmFn/6gr9D7HmPrQ==} + '@zag-js/utils@1.22.1': + resolution: {integrity: sha512-VXY4gjHaTENHW+wjnKKENZ2jcaW0vnG2a5lYEMuZR4dpNCKH217yFr/bCNrI44y2s1W3LWhWmpEjfZluP6udYg==} '@zkochan/js-yaml@0.0.7': resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} @@ -8896,6 +9051,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-tailwindcss@4.0.0-beta.0: + resolution: {integrity: sha512-WWCajZgQu38Sd67ZCl2W6i3MRzqB0d+H8s4qV9iB6lBJbsDOIpIlj6R1Fj2FXkoWErbo05pZnZYbCGIU9o/DsA==} + engines: {node: '>=18.12.0'} + peerDependencies: + tailwindcss: ^3.4.0 || ^4.0.0 + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -10720,6 +10881,27 @@ packages: sass: optional: true + next@15.5.2: + resolution: {integrity: sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -11929,6 +12111,10 @@ packages: resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.34.3: + resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -12269,6 +12455,16 @@ packages: peerDependencies: tailwindcss: '*' + tailwind-variants@3.1.0: + resolution: {integrity: sha512-ieiYaEfUr+sNhw/k++dosmZfVA4VIG5bV+G1eGdJSC4FcflqQv0iSIlOLj/RbzRuTu/VrIiNSlwh1esBM3BXUg==} + engines: {node: '>=16.x', pnpm: '>=7.x'} + peerDependencies: + tailwind-merge: '>=3.0.0' + tailwindcss: '*' + peerDependenciesMeta: + tailwind-merge: + optional: true + tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: @@ -14558,6 +14754,11 @@ snapshots: dependencies: tslib: 2.8.1 + '@emnapi/runtime@1.5.0': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.0.2': dependencies: tslib: 2.8.1 @@ -14827,6 +15028,10 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.10 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + '@floating-ui/dom@1.7.1': dependencies: '@floating-ui/core': 1.7.1 @@ -14837,6 +15042,11 @@ snapshots: '@floating-ui/core': 1.7.2 '@floating-ui/utils': 0.2.10 + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/dom': 1.7.1 @@ -15017,14 +15227,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@hyoban/eslint-plugin-tailwindcss@4.0.0-alpha.12(tailwindcss@4.1.8)': - dependencies: - fast-glob: 3.3.3 - postcss: 8.5.3 - synckit: 0.11.8 - tailwind-api-utils: 1.0.3(tailwindcss@4.1.8) - tailwindcss: 4.1.8 - '@iconify/json@2.2.338': dependencies: '@iconify/types': 2.0.0 @@ -15058,79 +15260,165 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.1.0 optional: true + '@img/sharp-darwin-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.0 + optional: true + '@img/sharp-darwin-x64@0.34.1': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.1.0 optional: true + '@img/sharp-darwin-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.0 + optional: true + '@img/sharp-libvips-darwin-arm64@1.1.0': optional: true + '@img/sharp-libvips-darwin-arm64@1.2.0': + optional: true + '@img/sharp-libvips-darwin-x64@1.1.0': optional: true + '@img/sharp-libvips-darwin-x64@1.2.0': + optional: true + '@img/sharp-libvips-linux-arm64@1.1.0': optional: true + '@img/sharp-libvips-linux-arm64@1.2.0': + optional: true + '@img/sharp-libvips-linux-arm@1.1.0': optional: true + '@img/sharp-libvips-linux-arm@1.2.0': + optional: true + '@img/sharp-libvips-linux-ppc64@1.1.0': optional: true + '@img/sharp-libvips-linux-ppc64@1.2.0': + optional: true + '@img/sharp-libvips-linux-s390x@1.1.0': optional: true + '@img/sharp-libvips-linux-s390x@1.2.0': + optional: true + '@img/sharp-libvips-linux-x64@1.1.0': optional: true + '@img/sharp-libvips-linux-x64@1.2.0': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.1.0': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + optional: true + '@img/sharp-linux-arm64@0.34.1': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.1.0 optional: true + '@img/sharp-linux-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.0 + optional: true + '@img/sharp-linux-arm@0.34.1': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.1.0 optional: true + '@img/sharp-linux-arm@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.0 + optional: true + + '@img/sharp-linux-ppc64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.0 + optional: true + '@img/sharp-linux-s390x@0.34.1': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.1.0 optional: true + '@img/sharp-linux-s390x@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.0 + optional: true + '@img/sharp-linux-x64@0.34.1': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.1.0 optional: true + '@img/sharp-linux-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.0 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.1': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 optional: true + '@img/sharp-linuxmusl-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + optional: true + '@img/sharp-linuxmusl-x64@0.34.1': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.1.0 optional: true + '@img/sharp-linuxmusl-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + optional: true + '@img/sharp-wasm32@0.34.1': dependencies: '@emnapi/runtime': 1.4.3 optional: true + '@img/sharp-wasm32@0.34.3': + dependencies: + '@emnapi/runtime': 1.5.0 + optional: true + + '@img/sharp-win32-arm64@0.34.3': + optional: true + '@img/sharp-win32-ia32@0.34.1': optional: true + '@img/sharp-win32-ia32@0.34.3': + optional: true + '@img/sharp-win32-x64@0.34.1': optional: true + '@img/sharp-win32-x64@0.34.3': + optional: true + '@inquirer/checkbox@2.5.0': dependencies: '@inquirer/core': 9.2.1 @@ -15182,7 +15470,7 @@ snapshots: dependencies: '@swc/helpers': 0.5.17 - '@internationalized/number@3.6.2': + '@internationalized/number@3.6.4': dependencies: '@swc/helpers': 0.5.17 @@ -16507,6 +16795,8 @@ snapshots: '@next/env@15.3.2': {} + '@next/env@15.5.2': {} + '@next/eslint-plugin-next@15.2.1': dependencies: fast-glob: 3.3.1 @@ -16521,48 +16811,72 @@ snapshots: '@next/swc-darwin-arm64@15.3.2': optional: true + '@next/swc-darwin-arm64@15.5.2': + optional: true + '@next/swc-darwin-x64@15.3.0': optional: true '@next/swc-darwin-x64@15.3.2': optional: true + '@next/swc-darwin-x64@15.5.2': + optional: true + '@next/swc-linux-arm64-gnu@15.3.0': optional: true '@next/swc-linux-arm64-gnu@15.3.2': optional: true + '@next/swc-linux-arm64-gnu@15.5.2': + optional: true + '@next/swc-linux-arm64-musl@15.3.0': optional: true '@next/swc-linux-arm64-musl@15.3.2': optional: true + '@next/swc-linux-arm64-musl@15.5.2': + optional: true + '@next/swc-linux-x64-gnu@15.3.0': optional: true '@next/swc-linux-x64-gnu@15.3.2': optional: true + '@next/swc-linux-x64-gnu@15.5.2': + optional: true + '@next/swc-linux-x64-musl@15.3.0': optional: true '@next/swc-linux-x64-musl@15.3.2': optional: true + '@next/swc-linux-x64-musl@15.5.2': + optional: true + '@next/swc-win32-arm64-msvc@15.3.0': optional: true '@next/swc-win32-arm64-msvc@15.3.2': optional: true + '@next/swc-win32-arm64-msvc@15.5.2': + optional: true + '@next/swc-win32-x64-msvc@15.3.0': optional: true '@next/swc-win32-x64-msvc@15.3.2': optional: true + '@next/swc-win32-x64-msvc@15.5.2': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -22659,286 +22973,251 @@ snapshots: js-yaml: 3.14.1 tslib: 2.8.1 - '@zag-js/accordion@1.15.0': - dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 - - '@zag-js/anatomy@1.15.0': {} - - '@zag-js/anatomy@1.18.2': {} - - '@zag-js/aria-hidden@1.15.0': {} - - '@zag-js/carousel@1.15.0': + '@zag-js/accordion@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/scroll-snap': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/checkbox@1.15.0': - dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/focus-visible': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy@1.22.1': {} - '@zag-js/collection@1.15.0': - dependencies: - '@zag-js/utils': 1.15.0 - - '@zag-js/combobox@1.15.0': - dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/aria-hidden': 1.15.0 - '@zag-js/collection': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dismissable': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/popper': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/aria-hidden@1.22.1': {} - '@zag-js/core@1.15.0': + '@zag-js/carousel@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/scroll-snap': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/core@1.18.2': + '@zag-js/checkbox@1.22.1': dependencies: - '@zag-js/dom-query': 1.18.2 - '@zag-js/utils': 1.18.2 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/focus-visible': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/dialog@1.15.0': + '@zag-js/collection@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/aria-hidden': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dismissable': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/focus-trap': 1.15.0 - '@zag-js/remove-scroll': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/utils': 1.22.1 - '@zag-js/dismissable@1.15.0': + '@zag-js/combobox@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 - '@zag-js/interact-outside': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/aria-hidden': 1.22.1 + '@zag-js/collection': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dismissable': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/popper': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/dismissable@1.18.2': + '@zag-js/core@1.22.1': dependencies: - '@zag-js/dom-query': 1.18.2 - '@zag-js/interact-outside': 1.18.2 - '@zag-js/utils': 1.18.2 + '@zag-js/dom-query': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/dom-query@1.15.0': + '@zag-js/dialog@1.22.1': dependencies: - '@zag-js/types': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/aria-hidden': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dismissable': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/focus-trap': 1.22.1 + '@zag-js/remove-scroll': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/dom-query@1.18.2': + '@zag-js/dismissable@1.22.1': dependencies: - '@zag-js/types': 1.18.2 + '@zag-js/dom-query': 1.22.1 + '@zag-js/interact-outside': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/focus-trap@1.15.0': + '@zag-js/dom-query@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 + '@zag-js/types': 1.22.1 - '@zag-js/focus-visible@1.15.0': + '@zag-js/focus-trap@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 + '@zag-js/dom-query': 1.22.1 - '@zag-js/i18n-utils@1.15.0': + '@zag-js/focus-visible@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 + '@zag-js/dom-query': 1.22.1 - '@zag-js/interact-outside@1.15.0': + '@zag-js/i18n-utils@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/dom-query': 1.22.1 - '@zag-js/interact-outside@1.18.2': + '@zag-js/interact-outside@1.22.1': dependencies: - '@zag-js/dom-query': 1.18.2 - '@zag-js/utils': 1.18.2 + '@zag-js/dom-query': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/menu@1.18.2': + '@zag-js/menu@1.22.1': dependencies: - '@zag-js/anatomy': 1.18.2 - '@zag-js/core': 1.18.2 - '@zag-js/dismissable': 1.18.2 - '@zag-js/dom-query': 1.18.2 - '@zag-js/popper': 1.18.2 - '@zag-js/rect-utils': 1.18.2 - '@zag-js/types': 1.18.2 - '@zag-js/utils': 1.18.2 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dismissable': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/popper': 1.22.1 + '@zag-js/rect-utils': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/number-input@1.15.0': + '@zag-js/number-input@1.22.1': dependencies: - '@internationalized/number': 3.6.2 - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@internationalized/number': 3.6.4 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/pagination@1.15.0': + '@zag-js/pagination@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/popover@1.15.0': + '@zag-js/popover@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/aria-hidden': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dismissable': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/focus-trap': 1.15.0 - '@zag-js/popper': 1.15.0 - '@zag-js/remove-scroll': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/aria-hidden': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dismissable': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/focus-trap': 1.22.1 + '@zag-js/popper': 1.22.1 + '@zag-js/remove-scroll': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/popper@1.15.0': + '@zag-js/popper@1.22.1': dependencies: - '@floating-ui/dom': 1.7.1 - '@zag-js/dom-query': 1.15.0 - '@zag-js/utils': 1.15.0 - - '@zag-js/popper@1.18.2': - dependencies: - '@floating-ui/dom': 1.7.2 - '@zag-js/dom-query': 1.18.2 - '@zag-js/utils': 1.18.2 + '@floating-ui/dom': 1.7.4 + '@zag-js/dom-query': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/rating-group@1.15.0': + '@zag-js/rating-group@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/react@1.15.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@zag-js/react@1.22.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@zag-js/core': 1.15.0 - '@zag-js/store': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/core': 1.22.1 + '@zag-js/store': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@zag-js/rect-utils@1.18.2': {} + '@zag-js/rect-utils@1.22.1': {} - '@zag-js/remove-scroll@1.15.0': + '@zag-js/remove-scroll@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 + '@zag-js/dom-query': 1.22.1 - '@zag-js/scroll-snap@1.15.0': + '@zag-js/scroll-snap@1.22.1': dependencies: - '@zag-js/dom-query': 1.15.0 + '@zag-js/dom-query': 1.22.1 - '@zag-js/select@1.15.0': + '@zag-js/select@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/collection': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dismissable': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/popper': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/collection': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dismissable': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/popper': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/slider@1.15.0': + '@zag-js/slider@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/steps@1.15.0': + '@zag-js/steps@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/store@1.15.0': + '@zag-js/store@1.22.1': dependencies: proxy-compare: 3.0.1 - '@zag-js/switch@1.15.0': + '@zag-js/switch@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/focus-visible': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/focus-visible': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/tabs@1.15.0': + '@zag-js/tabs@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/toast@1.15.0': + '@zag-js/toast@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dismissable': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dismissable': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/tooltip@1.15.0': + '@zag-js/tooltip@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/focus-visible': 1.15.0 - '@zag-js/popper': 1.15.0 - '@zag-js/store': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/focus-visible': 1.22.1 + '@zag-js/popper': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/tree-view@1.15.0': + '@zag-js/tree-view@1.22.1': dependencies: - '@zag-js/anatomy': 1.15.0 - '@zag-js/collection': 1.15.0 - '@zag-js/core': 1.15.0 - '@zag-js/dom-query': 1.15.0 - '@zag-js/types': 1.15.0 - '@zag-js/utils': 1.15.0 + '@zag-js/anatomy': 1.22.1 + '@zag-js/collection': 1.22.1 + '@zag-js/core': 1.22.1 + '@zag-js/dom-query': 1.22.1 + '@zag-js/types': 1.22.1 + '@zag-js/utils': 1.22.1 - '@zag-js/types@1.15.0': + '@zag-js/types@1.22.1': dependencies: csstype: 3.1.3 - '@zag-js/types@1.18.2': - dependencies: - csstype: 3.1.3 - - '@zag-js/utils@1.15.0': {} - - '@zag-js/utils@1.18.2': {} + '@zag-js/utils@1.22.1': {} '@zkochan/js-yaml@0.0.7': dependencies: @@ -24624,6 +24903,14 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-tailwindcss@4.0.0-beta.0(tailwindcss@4.1.8): + dependencies: + fast-glob: 3.3.3 + postcss: 8.5.3 + synckit: 0.11.8 + tailwind-api-utils: 1.0.3(tailwindcss@4.1.8) + tailwindcss: 4.1.8 + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -26785,6 +27072,32 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.5.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.84.0): + dependencies: + '@next/env': 15.5.2 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001718 + postcss: 8.4.31 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.27.1)(babel-plugin-macros@3.1.0)(react@19.1.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.2 + '@next/swc-darwin-x64': 15.5.2 + '@next/swc-linux-arm64-gnu': 15.5.2 + '@next/swc-linux-arm64-musl': 15.5.2 + '@next/swc-linux-x64-gnu': 15.5.2 + '@next/swc-linux-x64-musl': 15.5.2 + '@next/swc-win32-arm64-msvc': 15.5.2 + '@next/swc-win32-x64-msvc': 15.5.2 + '@opentelemetry/api': 1.9.0 + '@playwright/test': 1.52.0 + sass: 1.84.0 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -28506,6 +28819,36 @@ snapshots: '@img/sharp-win32-x64': 0.34.1 optional: true + sharp@0.34.3: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.3 + '@img/sharp-darwin-x64': 0.34.3 + '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-linux-arm': 0.34.3 + '@img/sharp-linux-arm64': 0.34.3 + '@img/sharp-linux-ppc64': 0.34.3 + '@img/sharp-linux-s390x': 0.34.3 + '@img/sharp-linux-x64': 0.34.3 + '@img/sharp-linuxmusl-arm64': 0.34.3 + '@img/sharp-linuxmusl-x64': 0.34.3 + '@img/sharp-wasm32': 0.34.3 + '@img/sharp-win32-arm64': 0.34.3 + '@img/sharp-win32-ia32': 0.34.3 + '@img/sharp-win32-x64': 0.34.3 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -28907,6 +29250,12 @@ snapshots: tailwind-merge: 3.0.2 tailwindcss: 4.1.8 + tailwind-variants@3.1.0(tailwind-merge@3.3.0)(tailwindcss@4.1.8): + dependencies: + tailwindcss: 4.1.8 + optionalDependencies: + tailwind-merge: 3.3.0 + tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.11.24(@swc/helpers@0.5.17))(@types/node@20.17.47)(typescript@5.8.3))): dependencies: tailwindcss: 3.4.17(ts-node@10.9.2(@swc/core@1.11.24(@swc/helpers@0.5.17))(@types/node@20.17.47)(typescript@5.8.3))