diff --git a/CLAUDE.md b/CLAUDE.md index a66fbbd..ff60c1a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,6 +41,23 @@ mcp__figma__get_design_context({ **Patterns**: Use native HTML elements, `.sr-only` for accessibility, `peer-*` modifiers for state styling, design tokens (`border-primary`, `bg-background`), avoid Radix UI unless complex state needed +5. **Update Vanilla Components** (IMPORTANT): + - Extract component classes: `node vanilla/extract-components.cjs ComponentName` + - Update relevant vanilla HTML files in `vanilla/components/` + - If new component: Create new `.html` file with examples + - If modified component: Update existing examples to match new styles/variants + - Add component to `vanilla/index.html` showcase if not already present + - Update `vanilla/README.md` with new component documentation + - Test: Open HTML files in browser, verify styling matches React version + +**Vanilla Component Files**: +- `vanilla/components/forms.html` - Checkbox, Radio, Select, Textarea +- `vanilla/components/ui-elements.html` - Badge, Avatar, Separator +- `vanilla/components/interactive.html` - Tabs, Accordion, Progress, Skeleton +- `vanilla/components/button.html`, `card.html`, `input.html`, `alert.html`, `dialog.html` + +**Important**: The vanilla HTML/CSS/JS version must stay in sync with React components for consistency across frameworks. + --- ## Adding a New Theme diff --git a/README.md b/README.md index 2200f58..28de0e5 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,87 @@ function MyCustomComponent() { **Note**: This is completely optional. The design system components work perfectly without any Tailwind setup in your project. +## Vanilla HTML/CSS/JS Version + +Can't use React? **We've got you covered!** The ATXP Design System includes a complete vanilla HTML/CSS/JavaScript version of all components for projects that can't use React. + +### Quick Start + +```html + + + + + + + + + + + + + + +``` + +### Available Components + +The vanilla version includes 20+ components: + +- **Form Components**: Checkbox, Radio, Select, Textarea, Input +- **UI Elements**: Badge, Avatar, Separator +- **Interactive**: Tabs, Accordion, Progress, Skeleton +- **Layout**: Button, Card, Alert, Dialog, Toast + +### Features + +- ✅ **Zero Dependencies** - Just HTML, CSS, and vanilla JavaScript +- ✅ **All 4 Themes** - Light, Dark, ATXP, DBG with live switching +- ✅ **Fully Accessible** - ARIA labels, keyboard navigation, screen reader support +- ✅ **Same Design** - Matches the React version pixel-perfect +- ✅ **Interactive Components** - Dialog, Tabs, Accordion, Toast with vanilla JS + +### Documentation + +- **Main Guide**: [`vanilla/README.md`](./vanilla/README.md) +- **Live Examples**: Open [`vanilla/index.html`](./vanilla/index.html) in your browser +- **Component Pages**: + - [`vanilla/components/forms.html`](./vanilla/components/forms.html) - Complete form elements + - [`vanilla/components/ui-elements.html`](./vanilla/components/ui-elements.html) - Badges, avatars, separators + - [`vanilla/components/interactive.html`](./vanilla/components/interactive.html) - Tabs, accordions, progress + - And more individual component examples + +### Theme Switching + +```html + + + + + +``` + +### Extraction Tool + +Keep vanilla components in sync with React: + +```bash +# Extract a single component's classes +node vanilla/extract-components.cjs Button + +# Extract all components +node vanilla/extract-components.cjs --all +``` + +See [`vanilla/README.md`](./vanilla/README.md) for complete documentation, examples, and JavaScript API reference. + ## Development ### Prerequisites diff --git a/render.yaml b/render.yaml index ab62915..edd77ca 100644 --- a/render.yaml +++ b/render.yaml @@ -6,4 +6,13 @@ services: buildCommand: pnpm install --frozen-lockfile && pnpm run build-storybook staticPublishPath: storybook-static autoDeployTrigger: commit + +- type: web + name: design-system-vanilla + runtime: static + repo: https://github.com/atxp-dev/design-system + buildCommand: pnpm install --frozen-lockfile && chmod +x vanilla/build.sh && ./vanilla/build.sh + staticPublishPath: vanilla + autoDeployTrigger: commit + version: "1" diff --git a/vanilla/.gitignore b/vanilla/.gitignore new file mode 100644 index 0000000..70bc4ec --- /dev/null +++ b/vanilla/.gitignore @@ -0,0 +1,14 @@ +# Build artifacts +dist/ +*.tar.gz +*.zip + +# Backup files +*.bak +*~ + +# Extracted component data +extracted/ + +# OS files +.DS_Store diff --git a/vanilla/README.md b/vanilla/README.md new file mode 100644 index 0000000..3a11f42 --- /dev/null +++ b/vanilla/README.md @@ -0,0 +1,519 @@ +# ATXP Design System - Vanilla HTML/CSS/JS + +A complete collection of design system components built with pure HTML, CSS, and vanilla JavaScript. **No React required!** + +This directory contains vanilla HTML/CSS/JS versions of the ATXP Design System components, perfect for projects that cannot use React or prefer a framework-free approach. + +## 🚀 Quick Start + +### Option 1: Download the Archive + +Download the complete vanilla package as a compressed archive: + +```bash +# The build script creates: atxp-design-system-vanilla.tar.gz (~136KB) +# Contains: HTML files, CSS, JavaScript, and all components + +tar -xzf atxp-design-system-vanilla.tar.gz +cd atxp-design-system-vanilla +# Open index.html in your browser! +``` + +The archive includes everything you need to get started with zero configuration. + +### Option 2: Manual Setup + +### 1. Include the CSS + +```html + +``` + +The `styles.css` file contains: +- All Tailwind CSS utilities +- Design tokens (colors, spacing, typography) +- All 4 theme definitions (light, dark, ATXP, DBG) + +### 2. Include the JavaScript (for interactive components) + +```html + +``` + +The `components.js` file provides: +- `Dialog` - Modal dialog functionality +- `ThemeSwitcher` - Theme management and persistence +- `Toast` - Toast notification system +- Auto-initialization for data attributes + +### 3. Set the Theme + +Add the `data-theme` attribute to your `` tag: + +```html + +``` + +### 4. Use Components + +Copy the HTML markup from the component examples: + +```html + +``` + +## 📁 Directory Structure + +``` +vanilla/ +├── index.html # Showcase page with all components +├── README.md # This file +├── components/ # Individual component examples +│ ├── button.html +│ ├── card.html +│ ├── input.html +│ ├── alert.html +│ └── dialog.html +├── js/ +│ └── components.js # Vanilla JS implementations +├── css/ # (Optional) Additional styles +└── extracted/ # Auto-generated extraction data + ├── Button.json + ├── Button.html + └── ... + +extract-components.cjs # Script to extract class names from React components +``` + +## 🎨 Available Components + +### Static Components (HTML/CSS only) + +- **Button** - Interactive buttons with multiple variants and sizes +- **Card** - Container components with header, content, and footer +- **Input** - Form inputs with various types and icon support +- **Alert** - Notification and message components + +### Interactive Components (Requires JavaScript) + +- **Dialog** - Modal dialogs with overlay and keyboard support +- **Toast** - Toast notifications (success, error, info) +- **Theme Switcher** - Live theme switching with localStorage persistence + +## 📋 Component List + +### Form Components +- **Checkbox** - Checkboxes for multi-select options +- **Radio** - Radio buttons for single-select options +- **Select** - Dropdown select menus +- **Textarea** - Multi-line text input +- **Input** - Single-line text inputs with icon support + +### UI Elements +- **Badge** - Small labels for status, tags, or counts (5 variants, 2 sizes) +- **Avatar** - User profile images with fallback initials +- **Separator** - Horizontal and vertical content dividers + +### Interactive Components +- **Tabs** - Tabbed content with vanilla JS +- **Accordion** - Collapsible content sections +- **Progress** - Progress bars with percentage indicators +- **Skeleton** - Loading placeholders + +### Layout Components +- **Button** - Buttons with multiple variants and sizes +- **Card** - Container cards with header/content/footer +- **Alert** - Notification and message components +- **Dialog** - Modal dialogs with overlay + +## 🎯 Component Examples + +### Button + +```html + + + + + + + + +``` + +### Checkbox + +```html +
+
+ + +
+ +
+``` + +### Badge + +```html + +
+ New +
+ + +
+ Active +
+ + +
+ Pending +
+``` + +### Avatar + +```html + +
+ User +
+ + +
+
+ JD +
+
+``` + +### Tabs + +```html + +
+ + +
+ + +
+ Account content here +
+
+ Password content here +
+ + +``` + +### Accordion + +```html +
+ +
+
Answer content here
+
+
+``` + +### Progress + +```html +
+
+
+``` + +### Card + +```html +
+ +
+

+ Card Title +

+

+ Card Description +

+
+ + +
+

Card content goes here.

+
+ + +
+ +
+
+``` + +### Dialog + +```html + + + + + +``` + +## 🔧 JavaScript API + +### Dialog + +```javascript +// Create a dialog instance +const dialog = new Dialog('my-dialog'); + +// Open the dialog +dialog.open(); + +// Close the dialog +dialog.close(); + +// Toggle the dialog +dialog.toggle(); + +// Listen for events +document.getElementById('my-dialog').addEventListener('dialog:open', (e) => { + console.log('Dialog opened:', e.detail.dialogId); +}); + +document.getElementById('my-dialog').addEventListener('dialog:close', (e) => { + console.log('Dialog closed:', e.detail.dialogId); +}); +``` + +### ThemeSwitcher + +```javascript +// Create a theme switcher instance +const themeSwitcher = new ThemeSwitcher(); + +// Get current theme +const currentTheme = themeSwitcher.getTheme(); + +// Set theme +themeSwitcher.setTheme('dark'); + +// Dispatch theme change event (triggers all listeners) +document.dispatchEvent(new CustomEvent('theme:change', { + detail: { theme: 'dark' } +})); +``` + +### Toast + +```javascript +// Show a toast +window.toast.show('This is a message!', { + duration: 3000, + variant: 'default', // 'default', 'success', 'destructive' + title: 'Optional Title' +}); + +// Success toast +window.toast.show('Operation completed!', { + variant: 'success', + title: 'Success' +}); + +// Error toast +window.toast.show('Something went wrong', { + variant: 'destructive', + title: 'Error' +}); +``` + +## 🎨 Theme System + +The design system includes 4 built-in themes: + +1. **Light** (default) - Clean, bright theme +2. **Dark** - Dark mode with high contrast +3. **ATXP** - ATXP brand theme with black primary color +4. **DBG** - DBG brand theme with cyan/teal accent +5. **Auto** - Automatically switches between light/dark based on system preference + +### Theme Switching + +#### Using Data Attributes + +```html + + + + + + +``` + +#### Programmatically + +```javascript +// Change theme via JavaScript +window.themeSwitcher.setTheme('dark'); + +// Or dispatch event +document.dispatchEvent(new CustomEvent('theme:change', { + detail: { theme: 'dark' } +})); +``` + +### Custom Themes + +To create a custom theme, define CSS variables with the `data-theme` attribute: + +```css +[data-theme="custom"] { + --theme-primary: #ff0000; + --theme-primary-foreground: #ffffff; + --theme-background: #ffffff; + --theme-foreground: #000000; + /* ... all other theme tokens */ +} +``` + +See `src/styles/themes/` for complete token lists. + +## 🛠️ Extraction Script + +Use the extraction script to automatically generate HTML templates from React components: + +```bash +# Extract a single component +node vanilla/extract-components.cjs Button + +# Extract all components +node vanilla/extract-components.cjs --all +``` + +The script generates: +- JSON files with extracted class names and variants +- HTML template files +- A comprehensive class reference guide + +## 📦 Distribution + +### For Static Sites + +1. Copy `/dist/styles.css` to your project +2. Copy `/vanilla/js/components.js` to your project +3. Copy component markup from `/vanilla/components/` + +### For CDN Usage + +```html + + + + + +``` + +## 🔍 Browser Support + +- Chrome/Edge (latest 2 versions) +- Firefox (latest 2 versions) +- Safari (latest 2 versions) + +Requires: +- CSS Variables support +- ES6+ JavaScript features +- LocalStorage (for theme persistence) + +## 📚 Full Documentation + +- [Main Documentation](../README.md) +- [Theme Guide](../THEMES.md) +- [Component Examples](./index.html) +- [Individual Components](./components/) + +## 🤝 Contributing + +To add a new vanilla component: + +1. Create a new HTML file in `components/` +2. Extract classes using `extract-components.cjs` +3. Add JavaScript functionality to `js/components.js` if needed +4. Update this README with examples +5. Add link to main `index.html` showcase + +## 📄 License + +MIT License - see [LICENSE](../LICENSE) for details + +## 🙋 Support + +For issues and questions: +- GitHub Issues: https://github.com/atxp/design-system/issues +- Documentation: https://design-system.atxp.com + +--- + +**Built with ❤️ using HTML, CSS, and vanilla JavaScript** diff --git a/vanilla/build.sh b/vanilla/build.sh new file mode 100755 index 0000000..66dbb57 --- /dev/null +++ b/vanilla/build.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# ATXP Design System - Vanilla Build Script +# This script prepares the vanilla HTML/CSS/JS site for deployment + +set -e + +echo "🚀 Building ATXP Design System - Vanilla Version" +echo "================================================" + +# Step 1: Build the main design system (generates dist/styles.css) +echo "📦 Step 1: Building design system..." +cd .. +pnpm build:css +pnpm build +cd vanilla + +# Step 2: Copy dist/ into vanilla/dist/ +echo "📁 Step 2: Copying dist/ files into vanilla..." +rm -rf dist +cp -r ../dist ./dist + +# Step 3: Update all references from ../dist to ./dist +echo "🔄 Step 3: Updating file references..." + +# Update all HTML files +for file in *.html components/*.html; do + if [ -f "$file" ]; then + echo " Updating $file" + # macOS sed requires -i '' for in-place editing + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's|href="../dist/|href="./dist/|g' "$file" + sed -i '' 's|src="../dist/|src="./dist/|g' "$file" + else + sed -i 's|href="../dist/|href="./dist/|g' "$file" + sed -i 's|src="../dist/|src="./dist/|g' "$file" + fi + fi +done + +# Step 4: Create downloadable GZIP archive +echo "📦 Step 4: Creating downloadable archive..." + +# Create a temporary directory for the archive contents +TEMP_DIR="atxp-design-system-vanilla" +rm -rf "$TEMP_DIR" +mkdir -p "$TEMP_DIR" + +# Copy all files to the temp directory +cp -r dist "$TEMP_DIR/" +cp -r components "$TEMP_DIR/" +cp -r js "$TEMP_DIR/" +cp *.html "$TEMP_DIR/" 2>/dev/null || true +[ -f README.md ] && cp README.md "$TEMP_DIR/" + +# Create the tar.gz file +ARCHIVE_NAME="atxp-design-system-vanilla.tar.gz" +tar -czf "$ARCHIVE_NAME" "$TEMP_DIR" + +# Clean up temp directory +rm -rf "$TEMP_DIR" + +echo "✅ Build complete!" +echo "" +echo "📊 Build output:" +echo " - vanilla/dist/ ($(du -sh dist 2>/dev/null | cut -f1) - CSS and assets)" +echo " - vanilla/*.html ($(ls -1 *.html | wc -l | tr -d ' ') files)" +echo " - vanilla/components/*.html ($(ls -1 components/*.html 2>/dev/null | wc -l | tr -d ' ') files)" +echo " - vanilla/js/*.js ($(ls -1 js/*.js 2>/dev/null | wc -l | tr -d ' ') file)" +echo " - $ARCHIVE_NAME ($(du -sh $ARCHIVE_NAME 2>/dev/null | cut -f1))" +echo "" +echo "🌐 Site ready for deployment from ./vanilla directory" +echo "📥 Download archive: ./$ARCHIVE_NAME" diff --git a/vanilla/components/alert.html b/vanilla/components/alert.html new file mode 100644 index 0000000..311bb61 --- /dev/null +++ b/vanilla/components/alert.html @@ -0,0 +1,126 @@ + + + + + + Alert Component - ATXP Design System + + + + +

Alert Component

+ + +
+

Default Alert

+
+ + + +
+
+ + +
+

Destructive Alert

+
+ + + +
+
+ + +
+

Simple Alerts

+
+ + + +
+
+ + + diff --git a/vanilla/components/button.html b/vanilla/components/button.html new file mode 100644 index 0000000..4eef529 --- /dev/null +++ b/vanilla/components/button.html @@ -0,0 +1,109 @@ + + + + + + Button Component - ATXP Design System + + + + +

Button Component

+ + +
+

Variants

+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + +
+

Sizes

+
+ + + + + + + +
+
+ + +
+

Disabled State

+
+ +
+
+ + + diff --git a/vanilla/components/card.html b/vanilla/components/card.html new file mode 100644 index 0000000..2c95574 --- /dev/null +++ b/vanilla/components/card.html @@ -0,0 +1,135 @@ + + + + + + Card Component - ATXP Design System + + + + +

Card Component

+ + +
+

Basic Card

+
+
+
+

Card Title

+

Card Description

+
+
+

This is the card content. You can put any content here.

+
+
+
+
+ + +
+

Card with Footer

+
+
+
+

Create Project

+

Deploy your new project in one click.

+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+

Multiple Cards

+
+
+
+

Notifications

+

You have 3 unread messages.

+
+
+

Click the button below to view all notifications.

+
+
+ +
+
+ +
+
+

Team Members

+

Invite your team to collaborate.

+
+
+

Manage your team members and their roles.

+
+
+ +
+
+ +
+
+

Settings

+

Configure your preferences.

+
+
+

Customize your application settings and preferences.

+
+
+ +
+
+
+
+ + + diff --git a/vanilla/components/dialog.html b/vanilla/components/dialog.html new file mode 100644 index 0000000..2d20a68 --- /dev/null +++ b/vanilla/components/dialog.html @@ -0,0 +1,252 @@ + + + + + + Dialog Component - ATXP Design System + + + + +

Dialog Component

+ + +
+

Dialog Examples

+
+ + + + + +
+
+ + + + + + + + + + + + + + + + diff --git a/vanilla/components/forms.html b/vanilla/components/forms.html new file mode 100644 index 0000000..1a9135d --- /dev/null +++ b/vanilla/components/forms.html @@ -0,0 +1,392 @@ + + + + + + Form Components - ATXP Design System + + + + +

Form Components

+ + +
+

Checkbox

+

Checkboxes for selecting multiple options.

+ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+

Radio Buttons

+

Radio buttons for selecting a single option from a group.

+ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+

Textarea

+

Multi-line text input for longer content.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Select

+

Dropdown select for choosing from a list of options.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Complete Form Example

+

All form components working together.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ +
+ + +
+ +
+
+ + +
+ +
+ +
+ + +
+
+
+ + + diff --git a/vanilla/components/input.html b/vanilla/components/input.html new file mode 100644 index 0000000..42d6424 --- /dev/null +++ b/vanilla/components/input.html @@ -0,0 +1,184 @@ + + + + + + Input Component - ATXP Design System + + + + +

Input Component

+ + +
+

Basic Inputs

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Input with Helper Text

+
+
+ + + Choose a unique username for your account. +
+
+
+ + +
+

Disabled Input

+
+
+ + +
+
+
+ + +
+

Input with Icon

+
+
+ +
+ + + + + +
+
+ +
+ +
+ + + + + +
+
+
+
+ + +
+

File Input

+
+
+ + +
+
+
+ + + diff --git a/vanilla/components/interactive.html b/vanilla/components/interactive.html new file mode 100644 index 0000000..9305766 --- /dev/null +++ b/vanilla/components/interactive.html @@ -0,0 +1,430 @@ + + + + + + Interactive Components - ATXP Design System + + + + +

Interactive Components

+ + +
+

Tabs

+

Organize content into separate views where only one view is visible at a time.

+ +
+ +
+ + + +
+ + +
+

Account Settings

+

+ Make changes to your account here. Click save when you're done. +

+
+
+ + +
+
+ + +
+ +
+
+ +
+

Change Password

+

+ Update your password here. After saving, you'll be logged out. +

+
+
+ + +
+
+ + +
+ +
+
+ +
+

Notification Settings

+

+ Configure how you receive notifications. +

+
+
+
+
Email Notifications
+
Receive email updates
+
+ +
+
+
+
Push Notifications
+
Receive push notifications
+
+ +
+ +
+
+
+
+ + +
+

Accordion

+

Collapsible content sections for organizing information.

+ +
+ +
+
+ +
+
+
+ The ATXP Design System is a comprehensive collection of reusable components, design tokens, and guidelines that help teams build consistent and accessible user interfaces quickly. +
+
+
+ + +
+
+ +
+
+
+ You can install the design system using npm or yarn:

+ npm install @atxp/design-system

+ Then import the CSS in your application. +
+
+
+ + +
+
+ +
+
+
+ Yes! The design system supports multiple themes (light, dark, ATXP, DBG) and you can easily create custom themes by defining CSS variables. All theme tokens use CSS custom properties for maximum flexibility. +
+
+
+ + +
+
+ +
+
+
+ Absolutely! All components are built with accessibility in mind, following WAI-ARIA guidelines. Components include proper ARIA labels, keyboard navigation, focus management, and screen reader support. +
+
+
+
+
+ + +
+

Progress

+

Visual indicator showing task completion.

+ +
+
+
+ Uploading... + 25% +
+
+
+
+
+ +
+
+ Processing... + 60% +
+
+
+
+
+ +
+
+ Complete! + 100% +
+
+
+
+
+ + +
+
+ Simulated Progress + 0% +
+
+
+
+ +
+
+
+ + +
+

Skeleton

+

Loading placeholders for content.

+ +
+
+

Card Skeleton

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+

List Skeleton

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/vanilla/components/ui-elements.html b/vanilla/components/ui-elements.html new file mode 100644 index 0000000..5ff49e7 --- /dev/null +++ b/vanilla/components/ui-elements.html @@ -0,0 +1,353 @@ + + + + + + UI Elements - ATXP Design System + + + + +

UI Elements

+ + +
+

Badges

+

Small labels for tags, status indicators, or counts.

+ +
+

Badge Variants

+
+ +
+ Default +
+ + +
+ Secondary +
+ + +
+ Destructive +
+ + +
+ Outline +
+ + +
+ Success +
+
+
+ +
+

Badge Sizes

+
+ +
+ Small +
+ + +
+ Medium +
+
+
+ +
+

Badge Use Cases

+
+
+ New +
+
+ Active +
+
+ Error +
+
+ Pending +
+
+ 99+ +
+
+
+
+ + +
+

Avatars

+

User profile images with fallback initials.

+ +
+

Avatar with Image

+
+ +
+ User +
+ +
+ User +
+ +
+ User +
+
+
+ +
+

Avatar with Fallback

+
+ +
+
+ JD +
+
+ +
+
+ AB +
+
+ +
+
+ XY +
+
+
+
+ +
+

Avatar Sizes

+
+ +
+ Small +
+ + +
+ Default +
+ + +
+ Large +
+ + +
+ Extra Large +
+
+
+ +
+

Avatar with Badge

+
+
+
+ User +
+ +
+
+ +
+
+
+ MK +
+
+ +
+
+ +
+
+ User +
+ +
+
+
+
+
+ + +
+

Separator

+

Visual dividers for content sections.

+ +
+

Horizontal Separator

+
+

Content above the separator

+ +

Content below the separator

+
+
+ +
+

Vertical Separator

+
+ Link 1 + + Link 2 + + Link 3 +
+
+ +
+

Separator in Card

+
+
+

Account Settings

+

Manage your account preferences

+
+ + + +
+
+
+
Email Notifications
+
Receive emails about your account activity
+
+ +
+ +
+
+
Marketing Emails
+
Receive emails about new features
+
+ +
+
+ + + +
+ + +
+
+
+
+ + +
+

Combined Example

+

Badges, avatars, and separators working together.

+ +
+
+

Team Members

+
+ + + +
+ +
+
+ User +
+
+
John Doe
+
john@example.com
+
+
+ Admin +
+
+ + + + +
+
+ User +
+
+
Jane Smith
+
jane@example.com
+
+
+ Member +
+
+ + + + +
+
+
+ RJ +
+
+
+
Robert Johnson
+
robert@example.com
+
+
+ Guest +
+
+
+
+
+ + + diff --git a/vanilla/extract-components.cjs b/vanilla/extract-components.cjs new file mode 100644 index 0000000..5bc72a9 --- /dev/null +++ b/vanilla/extract-components.cjs @@ -0,0 +1,333 @@ +#!/usr/bin/env node + +/** + * ATXP Design System - Component Extractor + * + * This script extracts component class names and variant patterns from React + * components to help generate vanilla HTML/CSS/JS versions. + * + * Usage: + * node extract-components.js + * node extract-components.js Button + * node extract-components.js --all + */ + +const fs = require('fs'); +const path = require('path'); + +// Configuration +const COMPONENTS_DIR = path.join(__dirname, '..', 'src', 'components'); +const OUTPUT_DIR = path.join(__dirname, 'extracted'); + +// Ensure output directory exists +if (!fs.existsSync(OUTPUT_DIR)) { + fs.mkdirSync(OUTPUT_DIR, { recursive: true }); +} + +/** + * Extract class names from a component file + */ +function extractClassNames(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const classNames = []; + + // Match className strings (both template literals and regular strings) + const classNameRegex = /className\s*=\s*{?\s*cn\s*\(\s*['"`]([^'"`]+)['"`]/g; + const simpleClassNameRegex = /className\s*=\s*['"`]([^'"`]+)['"`]/g; + + let match; + + // Extract cn() wrapped classNames + while ((match = classNameRegex.exec(content)) !== null) { + classNames.push({ + type: 'cn-wrapped', + classes: match[1].trim() + }); + } + + // Extract simple classNames + while ((match = simpleClassNameRegex.exec(content)) !== null) { + classNames.push({ + type: 'simple', + classes: match[1].trim() + }); + } + + return classNames; +} + +/** + * Extract variant definitions from CVA (class-variance-authority) + */ +function extractVariants(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const variants = {}; + + // Find CVA variant definitions + const cvaRegex = /const\s+\w+Variants\s*=\s*cva\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*{[\s\S]*?variants:\s*{([\s\S]*?)}\s*,/; + const match = cvaRegex.exec(content); + + if (match) { + const baseClasses = match[1].trim(); + const variantsBlock = match[2]; + + variants.base = baseClasses; + variants.variants = {}; + + // Extract each variant type + const variantRegex = /(\w+):\s*{([^}]+)}/g; + let variantMatch; + + while ((variantMatch = variantRegex.exec(variantsBlock)) !== null) { + const variantName = variantMatch[1].trim(); + const variantOptions = variantMatch[2]; + + variants.variants[variantName] = {}; + + // Extract each option within the variant + const optionRegex = /['"`]?(\w+)['"`]?\s*:\s*['"`]([^'"`]+)['"`]/g; + let optionMatch; + + while ((optionMatch = optionRegex.exec(variantOptions)) !== null) { + const optionName = optionMatch[1].trim(); + const optionClasses = optionMatch[2].trim(); + variants.variants[variantName][optionName] = optionClasses; + } + } + } + + return variants; +} + +/** + * Extract subcomponents from a component file + */ +function extractSubcomponents(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const subcomponents = []; + + // Find React.forwardRef components + const componentRegex = /const\s+(\w+)\s*=\s*React\.forwardRef[\s\S]*?className\s*=\s*{?\s*cn\s*\(\s*['"`]([^'"`]+)['"`]/g; + let match; + + while ((match = componentRegex.exec(content)) !== null) { + subcomponents.push({ + name: match[1], + classes: match[2].trim() + }); + } + + return subcomponents; +} + +/** + * Generate HTML template for a component + */ +function generateHTMLTemplate(componentName, data) { + let html = `\n\n`; + + if (data.variants && data.variants.base) { + html += `\n`; + html += `\n\n`; + + if (data.variants.variants) { + for (const [variantType, options] of Object.entries(data.variants.variants)) { + html += `\n`; + for (const [optionName, classes] of Object.entries(options)) { + html += `\n`; + html += `\n\n`; + } + } + } + } + + if (data.subcomponents && data.subcomponents.length > 0) { + html += `\n`; + data.subcomponents.forEach(sub => { + html += `\n`; + html += `
\n`; + html += ` ${sub.name} content\n`; + html += `
\n\n`; + }); + } + + return html; +} + +/** + * Process a single component + */ +function processComponent(componentName) { + const componentPath = path.join(COMPONENTS_DIR, componentName, `${componentName}.tsx`); + + if (!fs.existsSync(componentPath)) { + console.error(`❌ Component not found: ${componentName}`); + return; + } + + console.log(`\n📦 Processing ${componentName}...`); + + const classNames = extractClassNames(componentPath); + const variants = extractVariants(componentPath); + const subcomponents = extractSubcomponents(componentPath); + + const data = { + componentName, + classNames, + variants, + subcomponents + }; + + // Save extraction data as JSON + const jsonOutput = path.join(OUTPUT_DIR, `${componentName}.json`); + fs.writeFileSync(jsonOutput, JSON.stringify(data, null, 2)); + console.log(`✅ Saved extraction data: ${jsonOutput}`); + + // Generate HTML template + const htmlTemplate = generateHTMLTemplate(componentName, data); + const htmlOutput = path.join(OUTPUT_DIR, `${componentName}.html`); + fs.writeFileSync(htmlOutput, htmlTemplate); + console.log(`✅ Generated HTML template: ${htmlOutput}`); + + // Print summary + console.log(`\n📊 Summary:`); + console.log(` Class names found: ${classNames.length}`); + console.log(` Has variants: ${!!variants.base}`); + console.log(` Subcomponents: ${subcomponents.length}`); + + if (variants.base) { + console.log(`\n Base classes: ${variants.base}`); + if (variants.variants) { + for (const [variantType, options] of Object.entries(variants.variants)) { + console.log(` ${variantType}: ${Object.keys(options).join(', ')}`); + } + } + } + + return data; +} + +/** + * Process all components + */ +function processAllComponents() { + const components = fs.readdirSync(COMPONENTS_DIR) + .filter(name => { + const componentPath = path.join(COMPONENTS_DIR, name); + return fs.statSync(componentPath).isDirectory(); + }); + + console.log(`\n🚀 Processing ${components.length} components...\n`); + + const results = {}; + components.forEach(componentName => { + try { + results[componentName] = processComponent(componentName); + } catch (error) { + console.error(`❌ Error processing ${componentName}:`, error.message); + } + }); + + // Save combined results + const combinedOutput = path.join(OUTPUT_DIR, '_all-components.json'); + fs.writeFileSync(combinedOutput, JSON.stringify(results, null, 2)); + console.log(`\n✅ Saved combined results: ${combinedOutput}`); + + return results; +} + +/** + * Generate a class reference guide + */ +function generateClassReference(results) { + let markdown = '# ATXP Design System - Vanilla Class Reference\n\n'; + markdown += 'This document provides a reference of all component class names extracted from the React components.\n\n'; + + for (const [componentName, data] of Object.entries(results)) { + if (!data) continue; + + markdown += `## ${componentName}\n\n`; + + if (data.variants && data.variants.base) { + markdown += `### Base Classes\n\n`; + markdown += '```html\n'; + markdown += data.variants.base + '\n'; + markdown += '```\n\n'; + + if (data.variants.variants) { + for (const [variantType, options] of Object.entries(data.variants.variants)) { + markdown += `### ${variantType} Variants\n\n`; + for (const [optionName, classes] of Object.entries(options)) { + markdown += `**${optionName}:**\n`; + markdown += '```html\n'; + markdown += classes + '\n'; + markdown += '```\n\n'; + } + } + } + } + + if (data.subcomponents && data.subcomponents.length > 0) { + markdown += `### Subcomponents\n\n`; + data.subcomponents.forEach(sub => { + markdown += `**${sub.name}:**\n`; + markdown += '```html\n'; + markdown += sub.classes + '\n'; + markdown += '```\n\n'; + }); + } + + markdown += '---\n\n'; + } + + const markdownOutput = path.join(OUTPUT_DIR, 'CLASS_REFERENCE.md'); + fs.writeFileSync(markdownOutput, markdown); + console.log(`\n✅ Generated class reference: ${markdownOutput}`); +} + +// Main execution +function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.log(` +ATXP Design System - Component Extractor + +Usage: + node extract-components.js Extract a single component + node extract-components.js --all Extract all components + +Examples: + node extract-components.js Button + node extract-components.js Card + node extract-components.js --all + `); + return; + } + + if (args[0] === '--all') { + const results = processAllComponents(); + generateClassReference(results); + } else { + processComponent(args[0]); + } + + console.log('\n✨ Done!\n'); +} + +// Run if called directly +if (require.main === module) { + main(); +} + +module.exports = { + processComponent, + processAllComponents, + extractClassNames, + extractVariants, + extractSubcomponents +}; diff --git a/vanilla/index.html b/vanilla/index.html new file mode 100644 index 0000000..addd161 --- /dev/null +++ b/vanilla/index.html @@ -0,0 +1,534 @@ + + + + + + ATXP Design System - Vanilla HTML/CSS/JS + + + + + +
+

ATXP Design System

+
+ + +
+
+ + +
+ +
+

Vanilla HTML/CSS/JS Components

+

+ A complete collection of design system components built with pure HTML, CSS, and vanilla JavaScript. + No React required! +

+
+ + View Components + + + View on GitHub + +
+
+ + +
+

Component Showcase

+

+ See how components work together in a cohesive interface. +

+ + +
+ + +
+
+
+
+

Welcome Back, User!

+

Here's what's happening with your projects today.

+
+
+ + Active + +
+ JD +
+
+
+
+
+ + +
+
+
+ Total Projects + 12 +
+
24
+
+ +
+
+ Completed + +3 +
+
18
+
+ +
+
+ In Progress + 6 +
+
6
+
+
+ + +
+
+
+ + + + + +
+
New Updates Available
+
Version 2.0 is now available with new features and improvements.
+
+
+
+ +
+
+ + + +
+
Deployment Successful
+
Your latest changes have been deployed to production.
+
+
+
+
+ + +
+
+

Quick Action

+

Create a new project or update settings.

+
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+ +
+
+
+
+ + +
+
+ + +
+

Available Actions

+
+ + + + + + +
+
+ +
+
+ + +
+

Detailed Component Examples

+

+ Explore comprehensive examples of each component type with detailed usage patterns. +

+ +
+ +
Buttons
+
All button variants, sizes, and states
+
+ + +
Cards
+
Card layouts with headers and footers
+
+ + +
Inputs
+
Text inputs with icons and validation
+
+ + +
Alerts
+
Notification and message components
+
+ + +
Dialogs
+
Modal dialogs with overlays
+
+ + +
Forms
+
Checkboxes, radios, selects, textareas
+
+ + +
UI Elements
+
Badges, avatars, and separators
+
+ + +
Interactive
+
Tabs, accordions, progress, skeletons
+
+
+
+ + +
+

Getting Started

+

+ Quick guide to using the vanilla components in your project. +

+ +
+
1. Include the CSS
+
+ <link rel="stylesheet" href="path/to/dist/styles.css"> +
+ +
2. Include the JavaScript (for interactive components)
+
+ <script src="path/to/vanilla/js/components.js"></script> +
+ +
3. Use the components
+
+ <button class="inline-flex items-center justify-center ... bg-primary text-primary-foreground ...">
+ Click Me
+</button>
+
+ +
4. Set the theme
+
+ <html data-theme="light"> <!-- or dark, atxp, dbg, auto --> +
+
+
+
+ + + + + + + + + + + diff --git a/vanilla/js/components.js b/vanilla/js/components.js new file mode 100644 index 0000000..4fea973 --- /dev/null +++ b/vanilla/js/components.js @@ -0,0 +1,340 @@ +/** + * ATXP Design System - Vanilla JavaScript Components + * + * This file contains vanilla JavaScript implementations of interactive components + * from the ATXP Design System. + */ + +// ============================================================================ +// Dialog Component +// ============================================================================ + +class Dialog { + constructor(dialogId) { + this.dialogId = dialogId; + this.dialog = document.getElementById(dialogId); + this.overlay = this.dialog?.querySelector('[data-dialog-overlay]'); + this.content = this.dialog?.querySelector('[data-dialog-content]'); + this.closeBtn = this.dialog?.querySelector('[data-dialog-close]'); + this.isOpen = false; + + this.init(); + } + + init() { + if (!this.dialog) return; + + // Close button click + this.closeBtn?.addEventListener('click', () => this.close()); + + // Overlay click (close on backdrop click) + this.overlay?.addEventListener('click', () => this.close()); + + // Prevent closing when clicking inside content + this.content?.addEventListener('click', (e) => e.stopPropagation()); + + // ESC key to close + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && this.isOpen) { + this.close(); + } + }); + } + + open() { + if (!this.dialog) return; + + this.dialog.style.display = 'block'; + document.body.style.overflow = 'hidden'; + this.isOpen = true; + + // Dispatch custom event + this.dialog.dispatchEvent(new CustomEvent('dialog:open', { detail: { dialogId: this.dialogId } })); + } + + close() { + if (!this.dialog) return; + + this.dialog.style.display = 'none'; + document.body.style.overflow = ''; + this.isOpen = false; + + // Dispatch custom event + this.dialog.dispatchEvent(new CustomEvent('dialog:close', { detail: { dialogId: this.dialogId } })); + } + + toggle() { + if (this.isOpen) { + this.close(); + } else { + this.open(); + } + } +} + +// ============================================================================ +// Theme Switcher +// ============================================================================ + +class ThemeSwitcher { + constructor() { + this.currentTheme = this.getStoredTheme() || 'light'; + this.storageKey = 'atxp-theme'; + this.init(); + } + + init() { + // Apply stored theme + this.applyTheme(this.currentTheme); + + // Listen for theme change events + document.addEventListener('theme:change', (e) => { + this.setTheme(e.detail.theme); + }); + } + + getStoredTheme() { + try { + return localStorage.getItem(this.storageKey); + } catch (e) { + return null; + } + } + + storeTheme(theme) { + try { + localStorage.setItem(this.storageKey, theme); + } catch (e) { + // Silent fail if localStorage is not available + } + } + + applyTheme(theme) { + // Handle 'auto' theme + if (theme === 'auto') { + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + const actualTheme = prefersDark ? 'dark' : 'light'; + document.documentElement.setAttribute('data-theme', actualTheme); + } else { + document.documentElement.setAttribute('data-theme', theme); + } + } + + setTheme(theme) { + this.currentTheme = theme; + this.applyTheme(theme); + this.storeTheme(theme); + + // Update all theme switcher controls + document.querySelectorAll('[data-theme-control]').forEach(control => { + if (control.tagName === 'SELECT') { + control.value = theme; + } else if (control.tagName === 'BUTTON') { + const controlTheme = control.getAttribute('data-theme-value'); + if (controlTheme === theme) { + control.classList.add('active'); + } else { + control.classList.remove('active'); + } + } + }); + } + + getTheme() { + return this.currentTheme; + } +} + +// ============================================================================ +// Toast Notifications +// ============================================================================ + +class Toast { + constructor() { + this.container = this.createContainer(); + this.toasts = []; + } + + createContainer() { + let container = document.getElementById('toast-container'); + if (!container) { + container = document.createElement('div'); + container.id = 'toast-container'; + container.style.cssText = ` + position: fixed; + bottom: 0; + right: 0; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + z-index: 100; + `; + document.body.appendChild(container); + } + return container; + } + + show(message, options = {}) { + const { + duration = 3000, + variant = 'default', // 'default', 'success', 'destructive' + title = null + } = options; + + const toast = document.createElement('div'); + toast.className = ` + rounded-lg border px-4 py-3 shadow-lg + ${variant === 'destructive' ? 'bg-destructive text-destructive-foreground border-destructive' : ''} + ${variant === 'success' ? 'bg-success text-success-foreground border-success' : ''} + ${variant === 'default' ? 'bg-background text-foreground border-border' : ''} + `; + toast.style.cssText = ` + min-width: 300px; + animation: slideIn 0.2s ease-out; + `; + + let content = ''; + if (title) { + content += `
${title}
`; + } + content += `
${message}
`; + + toast.innerHTML = content; + this.container.appendChild(toast); + this.toasts.push(toast); + + // Auto remove after duration + if (duration > 0) { + setTimeout(() => { + this.remove(toast); + }, duration); + } + + return toast; + } + + remove(toast) { + toast.style.animation = 'slideOut 0.2s ease-out'; + setTimeout(() => { + toast.remove(); + this.toasts = this.toasts.filter(t => t !== toast); + }, 200); + } +} + +// Add toast animations +if (!document.getElementById('toast-animations')) { + const style = document.createElement('style'); + style.id = 'toast-animations'; + style.textContent = ` + @keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } + } + @keyframes slideOut { + from { + transform: translateX(0); + opacity: 1; + } + to { + transform: translateX(100%); + opacity: 0; + } + } + `; + document.head.appendChild(style); +} + +// ============================================================================ +// Utility Functions +// ============================================================================ + +/** + * Initialize theme switcher controls + * Looks for elements with [data-theme-control] attribute + */ +function initializeThemeControls() { + const themeSwitcher = new ThemeSwitcher(); + + document.querySelectorAll('[data-theme-control]').forEach(control => { + if (control.tagName === 'SELECT') { + control.addEventListener('change', (e) => { + document.dispatchEvent(new CustomEvent('theme:change', { + detail: { theme: e.target.value } + })); + }); + } else if (control.tagName === 'BUTTON') { + control.addEventListener('click', (e) => { + const theme = control.getAttribute('data-theme-value'); + document.dispatchEvent(new CustomEvent('theme:change', { + detail: { theme } + })); + }); + } + }); + + return themeSwitcher; +} + +/** + * Initialize dialog triggers + * Looks for elements with [data-dialog-trigger] attribute + */ +function initializeDialogs() { + const dialogs = {}; + + // Initialize all dialogs + document.querySelectorAll('[data-dialog-id]').forEach(element => { + const dialogId = element.getAttribute('data-dialog-id'); + dialogs[dialogId] = new Dialog(dialogId); + }); + + // Setup triggers + document.querySelectorAll('[data-dialog-trigger]').forEach(trigger => { + const dialogId = trigger.getAttribute('data-dialog-trigger'); + trigger.addEventListener('click', () => { + if (dialogs[dialogId]) { + dialogs[dialogId].open(); + } + }); + }); + + return dialogs; +} + +// ============================================================================ +// Auto-initialize on DOMContentLoaded +// ============================================================================ + +document.addEventListener('DOMContentLoaded', () => { + // Initialize theme controls + if (document.querySelector('[data-theme-control]')) { + window.themeSwitcher = initializeThemeControls(); + } + + // Initialize dialogs + if (document.querySelector('[data-dialog-id]')) { + window.dialogs = initializeDialogs(); + } + + // Initialize global toast instance + window.toast = new Toast(); +}); + +// Export for module usage +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + Dialog, + ThemeSwitcher, + Toast, + initializeThemeControls, + initializeDialogs + }; +}