diff --git a/.cursor/rules/design.mdc b/.cursor/rules/design.mdc new file mode 100755 index 0000000..8ffdf08 --- /dev/null +++ b/.cursor/rules/design.mdc @@ -0,0 +1,777 @@ +--- +description: Use this rule when asked to do any frontend or UI design +globs: +alwaysApply: false +--- +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) + +--- +description: Use this rule when asked to do any frontend or UI design +globs: +alwaysApply: false +--- +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) \ No newline at end of file diff --git a/.superdesign/design_iterations/default_ui_darkmode.css b/.superdesign/design_iterations/default_ui_darkmode.css new file mode 100755 index 0000000..a84d505 --- /dev/null +++ b/.superdesign/design_iterations/default_ui_darkmode.css @@ -0,0 +1,521 @@ +/* ======================================== + Dark Mode UI Framework + A beautiful dark mode design system + ======================================== */ + +/* ======================================== + CSS Variables & Theme + ======================================== */ +:root { + /* Dark Mode Color Palette */ + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + + /* Spacing & Layout */ + --radius: 0.625rem; + --spacing-xs: 0.25rem; + --spacing-sm: 0.5rem; + --spacing-md: 0.75rem; + --spacing-lg: 1rem; + --spacing-xl: 1.5rem; + --spacing-2xl: 2rem; + --spacing-3xl: 3rem; + + /* Typography */ + --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-size-xs: 0.75rem; + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.125rem; + --font-size-xl: 1.25rem; + --font-size-2xl: 1.5rem; + --font-size-3xl: 1.875rem; + --font-size-4xl: 2.25rem; +} + +/* ======================================== + Base Styles + ======================================== */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: var(--background); + color: var(--foreground); + font-family: var(--font-family); + line-height: 1.6; + min-height: 100vh; +} + +html.dark { + color-scheme: dark; +} + +/* ======================================== + Layout Components + ======================================== */ +.container { + max-width: 64rem; + margin: 0 auto; + padding: var(--spacing-2xl) var(--spacing-lg); +} + +.container-sm { + max-width: 42rem; +} + +.container-lg { + max-width: 80rem; +} + +.grid { + display: grid; +} + +.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } +.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-cols-auto { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } + +.gap-sm { gap: var(--spacing-sm); } +.gap-md { gap: var(--spacing-md); } +.gap-lg { gap: var(--spacing-lg); } +.gap-xl { gap: var(--spacing-xl); } + +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.text-center { + text-align: center; +} + +/* ======================================== + Card Components + ======================================== */ +.card { + background-color: var(--card); + color: var(--card-foreground); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: calc(var(--radius) + 4px); + padding: var(--spacing-xl); + box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1); + transition: all 0.2s ease; +} + +.card:hover { + box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); +} + +/* ======================================== + Button Components + ======================================== */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing-sm); + white-space: nowrap; + border-radius: var(--radius); + font-size: var(--font-size-sm); + font-weight: 500; + transition: all 0.2s; + border: none; + cursor: pointer; + padding: var(--spacing-sm) var(--spacing-lg); + min-height: 2.25rem; + outline: none; + text-decoration: none; +} + +.btn:disabled { + pointer-events: none; + opacity: 0.5; +} + +.btn-primary { + background-color: var(--primary); + color: var(--primary-foreground); +} + +.btn-primary:hover { + background-color: rgba(236, 236, 236, 0.9); +} + +.btn-outline { + background-color: transparent; + border: 1px solid var(--border); + color: var(--foreground); +} + +.btn-outline:hover { + background-color: var(--accent); +} + +.btn-ghost { + background-color: transparent; + color: var(--foreground); +} + +.btn-ghost:hover { + background-color: var(--accent); +} + +.btn-destructive { + background-color: var(--destructive); + color: white; +} + +.btn-destructive:hover { + background-color: rgba(220, 38, 38, 0.9); +} + +/* Button Sizes */ +.btn-sm { + padding: var(--spacing-xs) var(--spacing-md); + font-size: var(--font-size-xs); + min-height: 2rem; +} + +.btn-lg { + padding: var(--spacing-md) var(--spacing-xl); + font-size: var(--font-size-base); + min-height: 2.75rem; +} + +.btn-icon { + padding: var(--spacing-sm); + width: 2.25rem; + height: 2.25rem; +} + +/* ======================================== + Form Components + ======================================== */ +.form-input { + width: 100%; + background: rgba(255, 255, 255, 0.15); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: var(--spacing-sm) var(--spacing-md); + color: var(--foreground); + font-size: var(--font-size-sm); + outline: none; + transition: all 0.2s; +} + +.form-input:focus { + border-color: var(--ring); + box-shadow: 0 0 0 3px rgba(136, 136, 136, 0.5); +} + +.form-input::placeholder { + color: var(--muted-foreground); +} + +/* ======================================== + Badge Components + ======================================== */ +.badge { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius); + border: 1px solid; + padding: 0.125rem var(--spacing-sm); + font-size: var(--font-size-xs); + font-weight: 500; + white-space: nowrap; +} + +/* Priority Badge Variants */ +.badge-priority-high { + background: rgba(127, 29, 29, 0.3); + color: rgb(252, 165, 165); + border: 1px solid rgba(153, 27, 27, 0.5); +} + +.badge-priority-medium { + background: rgba(120, 53, 15, 0.3); + color: rgb(252, 211, 77); + border: 1px solid rgba(146, 64, 14, 0.5); +} + +.badge-priority-low { + background: rgba(20, 83, 45, 0.3); + color: rgb(134, 239, 172); + border: 1px solid rgba(22, 101, 52, 0.5); +} + +/* ======================================== + Tab Components + ======================================== */ +.tab-list { + display: flex; + gap: var(--spacing-sm); + margin-bottom: var(--spacing-xl); +} + +.tab-button { + background-color: transparent; + border: 1px solid rgba(255, 255, 255, 0.2); + color: var(--foreground); + text-transform: capitalize; + font-weight: 500; + transition: all 0.2s ease; + padding: var(--spacing-sm) var(--spacing-md); + border-radius: var(--radius); + cursor: pointer; + font-size: var(--font-size-sm); +} + +.tab-button:hover { + background-color: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.3); +} + +.tab-button.active { + background-color: #f8f9fa !important; + color: #1a1a1a !important; + border-color: #f8f9fa !important; + font-weight: 600; +} + +.tab-button.active:hover { + background-color: #e9ecef !important; + border-color: #e9ecef !important; +} + +/* ======================================== + Typography + ======================================== */ +.text-xs { font-size: var(--font-size-xs); } +.text-sm { font-size: var(--font-size-sm); } +.text-base { font-size: var(--font-size-base); } +.text-lg { font-size: var(--font-size-lg); } +.text-xl { font-size: var(--font-size-xl); } +.text-2xl { font-size: var(--font-size-2xl); } +.text-3xl { font-size: var(--font-size-3xl); } +.text-4xl { font-size: var(--font-size-4xl); } + +.font-normal { font-weight: 400; } +.font-medium { font-weight: 500; } +.font-semibold { font-weight: 600; } +.font-bold { font-weight: 700; } + +.text-primary { color: var(--primary); } +.text-muted { color: var(--muted-foreground); } +.text-destructive { color: var(--destructive); } + +.gradient-text { + background: linear-gradient(to right, var(--primary), rgba(236, 236, 236, 0.6)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +/* ======================================== + Icon System + ======================================== */ +.icon { + width: 1rem; + height: 1rem; + fill: currentColor; + flex-shrink: 0; +} + +.icon-sm { width: 0.875rem; height: 0.875rem; } +.icon-lg { width: 1.25rem; height: 1.25rem; } +.icon-xl { width: 1.5rem; height: 1.5rem; } +.icon-2xl { width: 2rem; height: 2rem; } + +/* ======================================== + Interactive Components + ======================================== */ +.checkbox { + width: 1rem; + height: 1rem; + border: 1px solid var(--border); + border-radius: 4px; + cursor: pointer; + position: relative; + background: rgba(255, 255, 255, 0.15); + transition: all 0.2s; +} + +.checkbox:hover { + border-color: var(--ring); +} + +.checkbox.checked { + background-color: rgb(22, 163, 74); + border-color: rgb(22, 163, 74); +} + +.checkbox.checked::after { + content: '✓'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 0.75rem; + font-weight: bold; +} + +/* ======================================== + List Components + ======================================== */ +.list-item { + display: flex; + align-items: center; + gap: var(--spacing-lg); + padding: var(--spacing-lg); + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + transition: background-color 0.2s; +} + +.list-item:hover { + background-color: rgba(255, 255, 255, 0.025); +} + +.list-item:last-child { + border-bottom: none; +} + +.list-item.completed { + opacity: 0.6; +} + +/* ======================================== + Empty State Component + ======================================== */ +.empty-state { + text-align: center; + padding: var(--spacing-3xl) var(--spacing-lg); + color: var(--muted-foreground); +} + +.empty-state .icon { + width: 3rem; + height: 3rem; + margin: 0 auto var(--spacing-lg); + opacity: 0.5; +} + +/* ======================================== + Utility Classes + ======================================== */ +.hidden { display: none; } +.block { display: block; } +.flex { display: flex; } +.inline-flex { display: inline-flex; } + +.w-full { width: 100%; } +.h-full { height: 100%; } +.min-h-screen { min-height: 100vh; } + +.opacity-50 { opacity: 0.5; } +.opacity-60 { opacity: 0.6; } +.opacity-75 { opacity: 0.75; } + +.transition-all { transition: all 0.2s ease; } +.transition-colors { transition: color 0.2s ease, background-color 0.2s ease; } +.transition-opacity { transition: opacity 0.2s ease; } + +/* ======================================== + Responsive Design + ======================================== */ +@media (max-width: 768px) { + .container { + padding: var(--spacing-lg); + } + + .grid-cols-auto { + grid-template-columns: 1fr; + } + + .flex-col-mobile { + flex-direction: column; + } + + .text-center-mobile { + text-align: center; + } + + .gap-sm-mobile { gap: var(--spacing-sm); } + + .hidden-mobile { display: none; } + .block-mobile { display: block; } +} + +@media (max-width: 640px) { + .text-2xl { font-size: var(--font-size-xl); } + .text-3xl { font-size: var(--font-size-2xl); } + .text-4xl { font-size: var(--font-size-3xl); } + + .container { + padding: var(--spacing-lg) var(--spacing-sm); + } +} + +/* ======================================== + Animation Utilities + ======================================== */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +.animate-fade-in { + animation: fadeIn 0.3s ease-out; +} + +/* ======================================== + Focus & Accessibility + ======================================== */ +.focus-visible:focus-visible { + outline: 2px solid var(--ring); + outline-offset: 2px; +} + +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} \ No newline at end of file diff --git a/.superdesign/design_iterations/enhanced_aperonight_components.html b/.superdesign/design_iterations/enhanced_aperonight_components.html new file mode 100755 index 0000000..4b7332b --- /dev/null +++ b/.superdesign/design_iterations/enhanced_aperonight_components.html @@ -0,0 +1,1524 @@ + + + + + + Aperonight Design System - Component Library + + + + + + + + + + + + +
+
+
+

Aperonight Design System

+

A comprehensive collection of reusable components for premium event booking experiences.

+ + + +
+
+
+ +
+ +
+

Color Palette

+
+
Brand Colors
+
+
+
+
Primary
+
+
+
+
Accent
+
+
+
+
Success
+
+
+
+
Warning
+
+
+
+
Danger
+
+
+
+
+ + +
+

Typography

+
+
+

Heading 1 - Hero Title

+

Heading 2 - Section Title

+

Heading 3 - Subsection

+

Heading 4 - Component Title

+

Bold text and regular paragraph text with good readability and proper line spacing for optimal user experience.

+

Small text for captions and metadata information.

+
+
+
+ + +
+

Buttons

+
+
+
Primary Buttons
+
+ + + +
+
+ +
+
Secondary Buttons
+
+ + + +
+
+ +
+
Outline Buttons
+
+ + + +
+
+ +
+
Icon Buttons
+
+ + + +
+
+
+
+ + +
+

Badges & Status Indicators

+
+
+
Event Status Badges
+
+ Available + Limited + Sold Out + ★ Featured + VIP +
+
+ +
+
Notification Badges
+
+
+ + 3 +
+
+ + +
+
+
+
+
+ + +
+

Cards

+
+
+
+

Basic Card

+

Simple card with header and content

+
+
+

This is a basic card component that can be used for various content types. It includes proper spacing and hover effects.

+ +
+
+ +
+ Event +
+
+ ★ Featured + Available +
+

Networking Event

+

Join professionals for an evening of networking and insights.

+
+ €35 + +
+
+
+ +
+
User Profile Card
+
+
+ User +
+
+
+

Alex Johnson

+

Senior Developer

+
+ + + + + + 4.2 +
+
+
+
+ + +
+
+
+
+ + +
+

Form Elements

+
+
+
Input Fields
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+
+
+ +
+
Form Validation
+
+
+ + +
+ + Email is valid +
+
+ +
+ + +
+ + Password must be at least 8 characters +
+
+ +
+ + +
+ + Please complete phone number +
+
+
+
+
+
+ + +
+

Notifications & Alerts

+
+
+
+ + Event successfully created! Your attendees will receive confirmation emails. +
+ +
+ + Only 5 spots remaining for this event. Book quickly to secure your place. +
+ +
+ + Payment failed. Please check your card details and try again. +
+ +
+ + New features available! Check out our improved event discovery tools. +
+
+
+
+ + +
+

Navigation & Tabs

+
+
Tab Navigation
+
+
+ + + + +
+
+ +
+
+ +
+

You have 3 upcoming events

+

Next event starts in 2 days

+
+
+
+ +
+
+ +
+

12 events attended

+

Great networking history!

+
+
+
+ +
+
+ +
+

5 saved events

+

Events you're interested in

+
+
+
+ +
+
+ +
+

2 events hosting

+

Active event host

+
+
+
+
+
+ + +
+

Interactive Elements

+
+
+
Tooltips
+
+
+ +
This is a helpful tooltip
+
+
+ +
Configure your preferences
+
+
+
+ +
+
Progress Indicators
+
+
+
+ Event Setup + 75% +
+
+
+
+
+ +
+
+ Registrations + 45/60 +
+
+
+
+
+
+
+ +
+
Loading States
+
+
+
+
+
+
+
+ +
+
+ +
+
Avatar Groups
+
+
+
Event Attendees
+
+
+ User 1 +
+
+
+ User 2 +
+
+
JD
+
+5
+
+
+ +
+
Small Avatars
+
+
AB
+
CD
+
EF
+
+
+
+
+
+
+ + +
+

Data Display

+
+
+
Price Tags
+
+
€15
+
€35
+
€55
+
+ €60 + €45 +
+
+
+ +
+
Ratings
+
+
+ + + + + + 5.0 + (24 reviews) +
+ +
+ + + + + + 4.2 +
+ +
+ + + + + + 3.5 +
+
+
+ +
+
Statistics
+
+
+
42
+
Events
+
+
+
1.2K
+
Attendees
+
+
+
+ +
+
Key-Value Lists
+
+
+ Date + March 15, 2024 +
+
+ Duration + 3 hours +
+
+ Capacity + 50 people +
+
+ Price + €35 +
+
+
+
+
+ + +
+

Spacing System

+
+
+
+ --space-1 (4px) +
+
+
+ --space-2 (8px) +
+
+
+ --space-4 (16px) +
+
+
+ --space-8 (32px) +
+
+
+ --space-12 (48px) +
+
+
+ --space-16 (64px) +
+
+
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/.superdesign/design_iterations/enhanced_aperonight_home_with_finder.html b/.superdesign/design_iterations/enhanced_aperonight_home_with_finder.html new file mode 100755 index 0000000..30abdb4 --- /dev/null +++ b/.superdesign/design_iterations/enhanced_aperonight_home_with_finder.html @@ -0,0 +1,1189 @@ + + + + + + Aperonight - Discover Premium Afterwork Events + + + + + + + + + + + + +
+
+
+

Discover Premium Afterwork Events

+

Connect with professionals, explore unique venues, and create memorable experiences at carefully curated afterwork events in your city.

+ +
+ + +
+ +
+
+ 150+ + Events Monthly +
+
+ 5.2K + Active Members +
+
+ 200+ + Partner Venues +
+
+ 98% + Satisfaction +
+
+
+
+
+ + +
+
+
+
+

Find Your Perfect Event

+

Discover afterwork parties tailored to your preferences

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + to + +
+
+ + +
+
+
+
+ + + + + +
+
+
+

Why Choose Aperonight?

+

We curate premium experiences that connect professionals and create lasting relationships.

+
+ +
+
+
+ +
+

Premium Curation

+

Every event is carefully selected and designed to provide exceptional value and networking opportunities.

+
+ +
+
+ +
+

Secure & Trusted

+

Safe payments, verified venues, and trusted community with comprehensive insurance coverage.

+
+ +
+
+ +
+

Quality Networking

+

Connect with verified professionals, entrepreneurs, and industry leaders in intimate settings.

+
+ +
+
+ +
+

Instant Booking

+

Seamless reservation process with instant confirmation and easy event management.

+
+
+
+
+ + +
+
+
+
+ 150+ +
Monthly Events
+
+
+ 5.2K +
Active Members
+
+
+ 200+ +
Partner Venues
+
+
+ 98% +
Satisfaction Rate
+
+
+
+
+ + +
+
+
+

Ready to Join the Community?

+

Start discovering amazing events and connect with like-minded professionals in your city.

+
+ + +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.superdesign/design_iterations/enhanced_aperonight_theme.css b/.superdesign/design_iterations/enhanced_aperonight_theme.css new file mode 100755 index 0000000..6ea2303 --- /dev/null +++ b/.superdesign/design_iterations/enhanced_aperonight_theme.css @@ -0,0 +1,618 @@ +/* Enhanced Aperonight Design System - Event Booking Optimized */ +:root { + /* Enhanced Purple gradient system with more depth */ + --color-primary-50: #faf5ff; + --color-primary-100: #f3e8ff; + --color-primary-200: #e9d5ff; + --color-primary-300: #d8b4fe; + --color-primary-400: #c084fc; + --color-primary-500: #a855f7; + --color-primary-600: #9333ea; + --color-primary-700: #7e22ce; + --color-primary-800: #6b21a8; + --color-primary-900: #581c87; + --color-primary-950: #3b0764; /* Added for deeper contrast */ + + /* Enhanced Pink gradient for event highlights */ + --color-accent-300: #f9a8d4; + --color-accent-400: #f472b6; + --color-accent-500: #ec4899; + --color-accent-600: #db2777; + --color-accent-700: #be185d; /* Added for better hierarchy */ + + /* Enhanced Neutral system with warmer tones */ + --color-neutral-50: #f8fafc; + --color-neutral-100: #f1f5f9; + --color-neutral-200: #e2e8f0; + --color-neutral-300: #cbd5e1; + --color-neutral-400: #94a3b8; + --color-neutral-500: #64748b; + --color-neutral-600: #475569; + --color-neutral-700: #334155; + --color-neutral-800: #1e293b; + --color-neutral-900: #0f172a; + --color-neutral-950: #020617; /* Added for deeper backgrounds */ + + /* Event-specific semantic colors */ + --color-success-light: #dcfce7; + --color-success: #16a34a; + --color-success-dark: #15803d; + --color-warning-light: #fef3c7; + --color-warning: #f59e0b; + --color-warning-dark: #d97706; + --color-danger-light: #fee2e2; + --color-danger: #dc2626; + --color-danger-dark: #b91c1c; + + /* Event status colors */ + --color-event-featured: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + --color-event-available: var(--color-success); + --color-event-limited: var(--color-warning); + --color-event-sold-out: var(--color-danger); + --color-event-vip: linear-gradient(135deg, #ffd700 0%, #ffb347 100%); + + /* Enhanced Typography with better hierarchy */ + --font-sans: 'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + --font-display: 'Outfit', var(--font-sans); /* For headings and key content */ + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', monospace; + + /* Enhanced font sizes with golden ratio scaling */ + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.875rem; /* 30px */ + --text-4xl: 2.25rem; /* 36px */ + --text-5xl: 3rem; /* 48px - for hero sections */ + --text-6xl: 3.75rem; /* 60px - for major headings */ + + /* Enhanced spacing system */ + --space-px: 1px; + --space-0-5: 0.125rem; /* 2px */ + --space-1: 0.25rem; /* 4px */ + --space-1-5: 0.375rem; /* 6px */ + --space-2: 0.5rem; /* 8px */ + --space-2-5: 0.625rem; /* 10px */ + --space-3: 0.75rem; /* 12px */ + --space-3-5: 0.875rem; /* 14px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-7: 1.75rem; /* 28px */ + --space-8: 2rem; /* 32px */ + --space-9: 2.25rem; /* 36px */ + --space-10: 2.5rem; /* 40px */ + --space-11: 2.75rem; /* 44px */ + --space-12: 3rem; /* 48px */ + --space-14: 3.5rem; /* 56px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + --space-24: 6rem; /* 96px */ + + /* Enhanced border radius system */ + --radius-none: 0px; + --radius-sm: 0.25rem; /* 4px */ + --radius-md: 0.375rem; /* 6px */ + --radius: 0.5rem; /* 8px */ + --radius-lg: 0.75rem; /* 12px */ + --radius-xl: 1rem; /* 16px */ + --radius-2xl: 1.5rem; /* 24px */ + --radius-3xl: 2rem; /* 32px */ + --radius-full: 9999px; + + /* Enhanced shadow system with color variations */ + --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); + + /* Purple-tinted shadows for premium feel */ + --shadow-purple-sm: 0 1px 3px 0 rgb(147 51 234 / 0.1), 0 1px 2px -1px rgb(147 51 234 / 0.1); + --shadow-purple-md: 0 4px 6px -1px rgb(147 51 234 / 0.1), 0 2px 4px -2px rgb(147 51 234 / 0.1); + --shadow-purple-lg: 0 10px 15px -3px rgb(147 51 234 / 0.15), 0 4px 6px -4px rgb(147 51 234 / 0.1); + + /* Pink-tinted shadows for event highlights */ + --shadow-pink-sm: 0 1px 3px 0 rgb(236 72 153 / 0.1), 0 1px 2px -1px rgb(236 72 153 / 0.1); + --shadow-pink-md: 0 4px 6px -1px rgb(236 72 153 / 0.1), 0 2px 4px -2px rgb(236 72 153 / 0.1); + + /* Animation durations */ + --duration-fast: 0.15s; + --duration-normal: 0.2s; + --duration-slow: 0.3s; + --duration-slower: 0.5s; + + /* Easing functions */ + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in: cubic-bezier(0.4, 0, 1, 1); + --ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +/* Enhanced Component Styles */ + +/* Buttons with improved hierarchy */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--space-2); + font-family: var(--font-display); + font-weight: 600; + white-space: nowrap; + transition: all var(--duration-normal) var(--ease-in-out); + cursor: pointer; + border: none; + text-decoration: none; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; +} + +.btn-sm { + padding: var(--space-2) var(--space-3); + font-size: var(--text-sm); + border-radius: var(--radius-md); +} + +.btn-md { + padding: var(--space-3) var(--space-4); + font-size: var(--text-base); + border-radius: var(--radius); +} + +.btn-lg { + padding: var(--space-4) var(--space-6); + font-size: var(--text-lg); + border-radius: var(--radius-lg); +} + +.btn-primary { + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + color: white; + box-shadow: var(--shadow-purple-sm); +} + +.btn-primary:hover { + background: linear-gradient(135deg, var(--color-primary-700) 0%, var(--color-accent-600) 100%); + box-shadow: var(--shadow-purple-md); + transform: translateY(-1px); +} + +.btn-primary:active { + transform: translateY(0); + box-shadow: var(--shadow-purple-sm); +} + +.btn-secondary { + background: white; + color: var(--color-primary-600); + border: 1px solid var(--color-neutral-200); + box-shadow: var(--shadow-sm); +} + +.btn-secondary:hover { + background: var(--color-primary-50); + border-color: var(--color-primary-300); + box-shadow: var(--shadow-md); +} + +.btn-outline { + background: transparent; + color: var(--color-primary-600); + border: 2px solid var(--color-primary-600); +} + +.btn-outline:hover { + background: var(--color-primary-600); + color: white; + box-shadow: var(--shadow-purple-sm); +} + +.btn-ghost { + background: transparent; + color: var(--color-neutral-600); + border: none; +} + +.btn-ghost:hover { + background: var(--color-neutral-100); + color: var(--color-primary-600); +} + +/* Enhanced Cards */ +.card { + background: white; + border-radius: var(--radius-xl); + box-shadow: var(--shadow-sm); + border: 1px solid var(--color-neutral-200); + overflow: hidden; + transition: all var(--duration-normal) var(--ease-in-out); +} + +.card:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-2px); +} + +.card-interactive { + cursor: pointer; +} + +.card-interactive:hover { + box-shadow: var(--shadow-xl); + transform: translateY(-4px); + border-color: var(--color-primary-200); +} + +.card-header { + padding: var(--space-6); + border-bottom: 1px solid var(--color-neutral-200); +} + +.card-body { + padding: var(--space-6); +} + +.card-footer { + padding: var(--space-4) var(--space-6); + background: var(--color-neutral-50); + border-top: 1px solid var(--color-neutral-200); +} + +/* Event-specific cards */ +.event-card { + position: relative; + background: white; + border-radius: var(--radius-xl); + box-shadow: var(--shadow-md); + overflow: hidden; + transition: all var(--duration-slow) var(--ease-out); + border: 1px solid var(--color-neutral-200); +} + +.event-card:hover { + box-shadow: var(--shadow-2xl); + transform: translateY(-6px) scale(1.02); +} + +.event-card-featured { + border: 2px solid transparent; + background: linear-gradient(white, white) padding-box, + linear-gradient(135deg, var(--color-primary-600), var(--color-accent-500)) border-box; + box-shadow: var(--shadow-purple-lg); +} + +.event-card-featured::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: var(--color-event-featured); +} + +.event-card-image { + aspect-ratio: 16/9; + overflow: hidden; + position: relative; +} + +.event-card-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform var(--duration-slow) var(--ease-out); +} + +.event-card:hover .event-card-image img { + transform: scale(1.05); +} + +/* Enhanced Forms */ +.form-group { + margin-bottom: var(--space-6); +} + +.form-label { + display: block; + font-size: var(--text-sm); + font-weight: 600; + color: var(--color-neutral-700); + margin-bottom: var(--space-2); + font-family: var(--font-display); +} + +.form-input, +.form-select, +.form-textarea { + width: 100%; + padding: var(--space-3) var(--space-4); + font-size: var(--text-base); + color: var(--color-neutral-900); + background: white; + border: 1px solid var(--color-neutral-300); + border-radius: var(--radius-lg); + transition: all var(--duration-normal) var(--ease-in-out); + font-family: var(--font-sans); +} + +.form-input:focus, +.form-select:focus, +.form-textarea:focus { + outline: none; + border-color: var(--color-primary-500); + box-shadow: 0 0 0 3px rgb(168 85 247 / 0.1); + background: var(--color-primary-50); +} + +.form-error { + color: var(--color-danger); + font-size: var(--text-sm); + margin-top: var(--space-1); + display: flex; + align-items: center; + gap: var(--space-1); +} + +/* Enhanced Badges */ +.badge { + display: inline-flex; + align-items: center; + gap: var(--space-1); + padding: var(--space-1-5) var(--space-3); + font-size: var(--text-xs); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + border-radius: var(--radius-full); + font-family: var(--font-display); +} + +.badge-available { + background: var(--color-success-light); + color: var(--color-success-dark); +} + +.badge-limited { + background: var(--color-warning-light); + color: var(--color-warning-dark); + animation: pulse 2s infinite; +} + +.badge-sold-out { + background: var(--color-danger-light); + color: var(--color-danger-dark); +} + +.badge-featured { + background: linear-gradient(135deg, var(--color-primary-100), var(--color-accent-100)); + color: var(--color-primary-800); + border: 1px solid var(--color-primary-200); +} + +.badge-vip { + background: linear-gradient(135deg, #fef3c7, #fde68a); + color: #92400e; + border: 1px solid #fbbf24; + box-shadow: var(--shadow-xs); +} + +/* Enhanced Navigation */ +.nav { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(20px); + border-bottom: 1px solid var(--color-neutral-200); + box-shadow: var(--shadow-sm); + position: sticky; + top: 0; + z-index: 50; +} + +.nav-link { + display: inline-flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-3) var(--space-4); + font-weight: 500; + color: var(--color-neutral-600); + text-decoration: none; + border-radius: var(--radius-lg); + transition: all var(--duration-normal) var(--ease-in-out); + font-family: var(--font-display); +} + +.nav-link:hover, +.nav-link.active { + color: var(--color-primary-600); + background: var(--color-primary-50); + box-shadow: var(--shadow-sm); +} + +/* Enhanced Layout */ +.container { + max-width: 1280px; + margin: 0 auto; + padding: 0 var(--space-4); +} + +@media (min-width: 640px) { + .container { padding: 0 var(--space-6); } +} + +@media (min-width: 1024px) { + .container { padding: 0 var(--space-8); } +} + +.grid-responsive { + display: grid; + gap: var(--space-6); + grid-template-columns: 1fr; +} + +@media (min-width: 640px) { + .grid-responsive { grid-template-columns: repeat(2, 1fr); } +} + +@media (min-width: 1024px) { + .grid-responsive { grid-template-columns: repeat(3, 1fr); } +} + +.grid-events { + display: grid; + gap: var(--space-8); + grid-template-columns: 1fr; +} + +@media (min-width: 640px) { + .grid-events { grid-template-columns: repeat(2, 1fr); } +} + +@media (min-width: 1024px) { + .grid-events { grid-template-columns: repeat(3, 1fr); } +} + +@media (min-width: 1280px) { + .grid-events { grid-template-columns: repeat(4, 1fr); } +} + +/* Enhanced animations */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +.animate-fadeInUp { + animation: fadeInUp 0.6s var(--ease-out); +} + +.animate-slideInRight { + animation: slideInRight 0.4s var(--ease-out); +} + +.animate-pulse { + animation: pulse 2s infinite; +} + +.loading-shimmer { + background: linear-gradient(90deg, var(--color-neutral-100) 25%, var(--color-neutral-200) 50%, var(--color-neutral-100) 75%); + background-size: 200% 100%; + animation: shimmer 2s infinite; +} + +/* Enhanced hover effects */ +.hover-lift { + transition: transform var(--duration-normal) var(--ease-out); +} + +.hover-lift:hover { + transform: translateY(-2px); +} + +.hover-glow { + transition: all var(--duration-normal) var(--ease-out); +} + +.hover-glow:hover { + box-shadow: var(--shadow-purple-lg); + filter: brightness(1.05); +} + +/* Focus states with better accessibility */ +.focus-ring { + transition: all var(--duration-fast) var(--ease-out); +} + +.focus-ring:focus { + outline: none; + box-shadow: 0 0 0 3px rgb(168 85 247 / 0.2); +} + +/* Dark mode enhancements */ +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--color-neutral-900); + --color-surface: var(--color-neutral-800); + --color-border: var(--color-neutral-700); + --color-text-primary: var(--color-neutral-50); + --color-text-secondary: var(--color-neutral-300); + --color-text-muted: var(--color-neutral-500); + } + + .card { + background: var(--color-neutral-800); + border-color: var(--color-neutral-700); + } + + .form-input, + .form-select, + .form-textarea { + background: var(--color-neutral-700); + border-color: var(--color-neutral-600); + color: var(--color-neutral-100); + } + + .nav { + background: rgba(30, 41, 59, 0.95); + border-bottom-color: var(--color-neutral-700); + } +} + +/* Print styles */ +@media print { + .btn, + .nav, + .card:hover { + box-shadow: none !important; + transform: none !important; + } + + .event-card { + break-inside: avoid; + margin-bottom: var(--space-4); + } +} \ No newline at end of file diff --git a/.superdesign/design_iterations/neo_brutalist_home.html b/.superdesign/design_iterations/neo_brutalist_home.html new file mode 100755 index 0000000..380f8b5 --- /dev/null +++ b/.superdesign/design_iterations/neo_brutalist_home.html @@ -0,0 +1,627 @@ + + + + + + APERONIGHT - RADICAL EVENT BOOKING + + + + + + + + +
+ + + + + +
+
+

RADICAL EVENTS

+

BREAK THE BORING. JOIN THE REVOLUTION. EXPERIENCE EVENTS THAT MATTER.

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

FEATURED CHAOS

+
+
+ TECH CHAOS +
+

TECH CHAOS NIGHT

+
+ + THU MAR 15 • 6PM +
+

SMASH NETWORKING BARRIERS. CODE. DRINKS. CHAOS. REPEAT.

+ +
+
+ +
+ SOUND ASSAULT +
+

SOUND ASSAULT

+
+ + SAT MAR 18 • 8PM +
+

UNDERGROUND BEATS. ROOF ACCESS. CITY DOMINATION.

+ +
+
+ +
+ ART REBELLION +
+

ART REBELLION

+
+ + FRI MAR 22 • 7PM +
+

DESTROY CONVENTIONS. CREATE CHAOS. WINE INCLUDED.

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

WHY WE RULE

+
+
+
+ +
+

CURATED MADNESS

+

HANDPICKED EVENTS THAT DESTROY BORING AND CREATE LEGENDS.

+
+
+
+ +
+

BULLETPROOF BOOKING

+

SECURE PAYMENTS. INSTANT TICKETS. NO BULLSHIT REFUNDS.

+
+
+
+ +
+

ZERO FRICTION

+

FIND EVENT. BOOK TICKET. DESTROY EXPECTATIONS. REPEAT.

+
+
+
+ +
+

TRIBE BUILDING

+

CONNECT WITH REBELS WHO GET IT. BUILD YOUR EMPIRE.

+
+
+
+
+ + +
+
+
+
+ 50+ +
EVENTS MONTHLY
+
+
+ 2.5K +
REBELS JOINED
+
+
+ 100+ +
VENUES CONQUERED
+
+
+ 95% +
MINDS BLOWN
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.superdesign/design_iterations/neo_brutalist_theme.css b/.superdesign/design_iterations/neo_brutalist_theme.css new file mode 100755 index 0000000..0603d72 --- /dev/null +++ b/.superdesign/design_iterations/neo_brutalist_theme.css @@ -0,0 +1,417 @@ +/* Neo-Brutalist Design System for Event Booking */ +:root { + /* Colors - Bold and high contrast */ + --background: #ffffff; + --foreground: #000000; + --card: #ffffff; + --card-foreground: #000000; + --popover: #ffffff; + --popover-foreground: #000000; + --primary: #ff6b35; + --primary-foreground: #ffffff; + --secondary: #00f5ff; + --secondary-foreground: #000000; + --muted: #f0f0f0; + --muted-foreground: #333333; + --accent: #ff1744; + --accent-foreground: #ffffff; + --destructive: #000000; + --destructive-foreground: #ffffff; + --border: #000000; + --input: #ffffff; + --ring: #ff6b35; + + /* Event-specific colors - Bold and vibrant */ + --event-featured: #7c4dff; + --event-sold-out: #000000; + --event-available: #00c853; + --ticket-premium: #ffc107; + --ticket-standard: #9e9e9e; + + /* Typography - Bold and impactful */ + --font-sans: 'Space Grotesk', 'Arial Black', sans-serif; + --font-mono: 'Space Mono', 'Courier New', monospace; + + /* Font sizes - Exaggerated scale */ + --text-xs: 0.75rem; + --text-sm: 0.875rem; + --text-base: 1rem; + --text-lg: 1.25rem; + --text-xl: 1.5rem; + --text-2xl: 2rem; + --text-3xl: 2.5rem; + --text-4xl: 3.5rem; + + /* Spacing - Generous */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-12: 3rem; + --space-16: 4rem; + --space-20: 5rem; + + /* Border radius - Sharp edges */ + --radius: 0px; + --radius-sm: 0px; + --radius-md: 0px; + --radius-lg: 0px; + --radius-xl: 0px; + + /* Shadows - Bold and offset */ + --shadow-brutal: 8px 8px 0px 0px #000000; + --shadow-brutal-lg: 12px 12px 0px 0px #000000; + --shadow-brutal-xl: 16px 16px 0px 0px #000000; + --shadow-brutal-color: 8px 8px 0px 0px var(--accent); + --shadow-inset: inset 4px 4px 0px 0px rgba(0,0,0,0.2); +} + +/* Typography overrides */ +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-sans) !important; + font-weight: 900 !important; + text-transform: uppercase !important; + letter-spacing: -0.02em !important; +} + +h1 { font-size: var(--text-4xl) !important; } +h2 { font-size: var(--text-3xl) !important; } +h3 { font-size: var(--text-2xl) !important; } + +/* Component styles */ +.btn-primary { + background: var(--primary); + color: var(--primary-foreground); + padding: 16px 32px; + border-radius: var(--radius); + font-weight: 900; + border: 3px solid var(--border); + cursor: pointer; + transition: all 0.1s ease; + box-shadow: var(--shadow-brutal); + font-family: var(--font-sans); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.btn-primary:hover { + transform: translate(4px, 4px); + box-shadow: 4px 4px 0px 0px #000000; +} + +.btn-primary:active { + transform: translate(8px, 8px); + box-shadow: none; +} + +.btn-secondary { + background: var(--secondary); + color: var(--secondary-foreground); + padding: 16px 32px; + border-radius: var(--radius); + font-weight: 900; + border: 3px solid var(--border); + cursor: pointer; + transition: all 0.1s ease; + box-shadow: var(--shadow-brutal); + font-family: var(--font-sans); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.btn-secondary:hover { + transform: translate(4px, 4px); + box-shadow: 4px 4px 0px 0px #000000; +} + +.btn-destructive { + background: var(--destructive); + color: var(--destructive-foreground); + padding: 16px 32px; + border-radius: var(--radius); + font-weight: 900; + border: 3px solid var(--border); + cursor: pointer; + transition: all 0.1s ease; + box-shadow: var(--shadow-brutal-color); + font-family: var(--font-sans); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.card { + background: var(--card); + border-radius: var(--radius); + box-shadow: var(--shadow-brutal-lg); + padding: var(--space-8); + border: 4px solid var(--border); + transition: all 0.1s ease; + position: relative; +} + +.card::before { + content: ''; + position: absolute; + top: -4px; + left: -4px; + right: -16px; + bottom: -16px; + background: var(--accent); + z-index: -1; +} + +.card:hover { + transform: translate(4px, 4px); + box-shadow: 8px 8px 0px 0px #000000; +} + +.event-card { + background: var(--card); + border-radius: var(--radius); + overflow: visible; + box-shadow: var(--shadow-brutal-xl); + transition: all 0.1s ease; + border: 4px solid var(--border); + position: relative; +} + +.event-card::after { + content: ''; + position: absolute; + top: -8px; + left: -8px; + right: -24px; + bottom: -24px; + background: repeating-linear-gradient( + 45deg, + transparent, + transparent 10px, + var(--secondary) 10px, + var(--secondary) 20px + ); + z-index: -1; + opacity: 0; + transition: opacity 0.2s ease; +} + +.event-card:hover::after { + opacity: 1; +} + +.event-card:hover { + transform: translate(8px, 8px); + box-shadow: 8px 8px 0px 0px #000000; +} + +.ticket-card { + background: var(--muted); + border-radius: var(--radius); + padding: var(--space-6); + border: 3px solid var(--border); + transition: all 0.1s ease; + box-shadow: var(--shadow-brutal); + position: relative; +} + +.ticket-card.selected { + background: var(--primary); + color: var(--primary-foreground); + transform: translate(4px, 4px); + box-shadow: 4px 4px 0px 0px #000000; +} + +.ticket-card.selected::before { + content: '✓'; + position: absolute; + top: -10px; + right: -10px; + background: var(--accent); + color: white; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + font-weight: 900; + border: 3px solid var(--border); + box-shadow: 4px 4px 0px 0px #000000; +} + +.form-input { + background: var(--input); + border: 3px solid var(--border); + border-radius: var(--radius); + padding: 16px 20px; + font-size: var(--text-lg); + font-weight: 700; + transition: all 0.1s ease; + box-shadow: var(--shadow-inset); + font-family: var(--font-mono); +} + +.form-input:focus { + outline: none; + border-color: var(--primary); + box-shadow: var(--shadow-brutal-color); + transform: translate(-2px, -2px); +} + +.badge { + display: inline-flex; + align-items: center; + padding: 8px 16px; + border-radius: var(--radius); + font-size: var(--text-sm); + font-weight: 900; + text-transform: uppercase; + letter-spacing: 0.1em; + border: 2px solid var(--border); + box-shadow: 4px 4px 0px 0px #000000; + font-family: var(--font-sans); +} + +.badge-available { + background: var(--event-available); + color: #000000; +} + +.badge-sold-out { + background: var(--event-sold-out); + color: #ffffff; +} + +.badge-featured { + background: var(--event-featured); + color: #ffffff; + animation: pulse-brutal 2s infinite; +} + +.badge-premium { + background: var(--ticket-premium); + color: #000000; + position: relative; +} + +.badge-premium::after { + content: '★'; + margin-left: 8px; + animation: rotate 3s linear infinite; +} + +/* Navigation */ +.nav { + background: var(--background); + border-bottom: 6px solid var(--border); + box-shadow: 0 6px 0px 0px var(--accent); +} + +.nav-link { + color: var(--foreground); + font-weight: 900; + padding: 12px 20px; + border-radius: var(--radius); + transition: all 0.1s ease; + text-decoration: none; + text-transform: uppercase; + letter-spacing: 0.05em; + border: 3px solid transparent; + font-family: var(--font-sans); +} + +.nav-link:hover, .nav-link.active { + background: var(--primary); + color: var(--primary-foreground); + border-color: var(--border); + box-shadow: 4px 4px 0px 0px #000000; + transform: translate(-2px, -2px); +} + +/* Animations */ +@keyframes pulse-brutal { + 0%, 100% { + transform: scale(1); + box-shadow: 4px 4px 0px 0px #000000; + } + 50% { + transform: scale(1.05); + box-shadow: 6px 6px 0px 0px #000000; + } +} + +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +@keyframes shake-brutal { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-8px); } + 75% { transform: translateX(8px); } +} + +.animate-shake { + animation: shake-brutal 0.5s ease-in-out; +} + +/* Special effects */ +.glitch-text { + position: relative; + color: var(--primary); + font-weight: 900; +} + +.glitch-text::before, +.glitch-text::after { + content: attr(data-text); + position: absolute; + top: 0; + left: 0; + overflow: hidden; + clip-path: polygon(0 0, 100% 0, 100% 45%, 0 45%); +} + +.glitch-text::before { + color: var(--accent); + animation: glitch-1 2s infinite; +} + +.glitch-text::after { + color: var(--secondary); + animation: glitch-2 2s infinite; +} + +@keyframes glitch-1 { + 0%, 100% { transform: translateX(0); } + 20% { transform: translateX(-2px); } +} + +@keyframes glitch-2 { + 0%, 100% { transform: translateX(0); } + 20% { transform: translateX(2px); } +} + +/* Responsive design */ +@media (max-width: 768px) { + :root { + --space-4: 0.75rem; + --space-6: 1rem; + --space-8: 1.5rem; + --text-4xl: 2.5rem; + --text-3xl: 2rem; + } + + .btn-primary, .btn-secondary, .btn-destructive { + padding: 12px 24px; + font-size: var(--text-sm); + } + + .card { + padding: var(--space-4); + } + + .shadow-brutal, .shadow-brutal-lg, .shadow-brutal-xl { + box-shadow: 4px 4px 0px 0px #000000; + } +} \ No newline at end of file diff --git a/.windsurfrules b/.windsurfrules new file mode 100755 index 0000000..fe3c182 --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,767 @@ +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) + +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100755 index 0000000..c6ea163 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,766 @@ +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) + +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) \ No newline at end of file diff --git a/README.md b/README.md index 61d6690..8aa646d 100755 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -# Aperonight - Party Booking Platform +# Aperonight - Event Booking Platform ![Aperonight Screenshot](app/assets/images/screenshot-homepage.png) ## 🌃 Overview -**Aperonight** is a two-sided marketplace connecting party-goers with nightlife promoters in Paris. The platform allows: +**Aperonight** is a two-sided marketplace connecting event-goers with nightlife promoters in Paris. The platform allows: -- **Customers** to discover/book tickets for upcoming parties +- **Customers** to discover/book tickets for upcoming events - **Promoters** to create/manage events and validate tickets at venue entrances ## 🎯 Key Features -### For Party-Goers -✔ Browse upcoming parties with filters (date, location, music genre) +### For Event-Goers +✔ Browse upcoming events with filters (date, location, music genre) ✔ Book tickets with multiple bundle options (VIP, group passes, etc.) ✔ Secure payment processing (credit cards, Apple/Google Pay) ✔ Mobile-friendly e-tickets with QR codes @@ -52,13 +52,13 @@ erDiagram string email string encrypted_password } - PROMOTER ||--o{ PARTY : creates + PROMOTER ||--o{ EVENT : creates PROMOTER { integer id string stripe_account_id } - PARTY ||--o{ TICKET_TYPE : has - PARTY { + EVENT ||--o{ TICKET_TYPE : has + EVENT { integer id datetime start_time } diff --git a/app/assets/stylesheets/application.postcss.css b/app/assets/stylesheets/application.postcss.css index 39944f4..eb24773 100755 --- a/app/assets/stylesheets/application.postcss.css +++ b/app/assets/stylesheets/application.postcss.css @@ -3,10 +3,50 @@ /* Import Tailwind using PostCSS */ @import "tailwindcss"; -/* Import flash message styles */ -@import "components/flash"; +/* Import our custom theme */ +@import "theme"; -/** Default text color */ +/* Import components */ +@import "components/hero"; +@import "components/flash"; +@import "components/footer"; +@import "components/event-finder"; + +/* Import pages */ +@import "pages/home"; + +/* Base styles */ body { - color: #555555; + font-family: var(--font-sans); + line-height: 1.6; + color: var(--color-neutral-900); + background: var(--color-neutral-50); +} + +/* App wrapper */ +.app-wrapper { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Main content */ +main { + flex: 1; +} + +/* Footer */ +.footer { + background: var(--color-neutral-800); + color: var(--color-neutral-300); +} + +/* Flash messages */ +.flash { + width: 100%; +} + +/* Yield content */ +.yield { + width: 100%; } diff --git a/app/assets/stylesheets/components/event-finder.css b/app/assets/stylesheets/components/event-finder.css new file mode 100755 index 0000000..2f2c908 --- /dev/null +++ b/app/assets/stylesheets/components/event-finder.css @@ -0,0 +1,210 @@ +.event-finder { + background: white; + border-radius: var(--radius-2xl); + box-shadow: var(--shadow-2xl); + padding: var(--space-8); + margin: var(--space-12) auto; + position: relative; + z-index: 10; + border: 1px solid var(--color-neutral-200); + backdrop-filter: blur(20px); +} + +.finder-header { + text-align: center; + margin-bottom: var(--space-8); +} + +.finder-title { + font-family: var(--font-display); + font-size: var(--text-2xl); + font-weight: 800; + margin-bottom: var(--space-2); + color: var(--color-neutral-900); +} + +.finder-subtitle { + color: var(--color-neutral-600); + font-size: var(--text-lg); +} + +.finder-form { + display: grid; + gap: var(--space-6); + grid-template-columns: 1fr; +} + +@media (min-width: 768px) { + .finder-form { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1024px) { + .finder-form { + grid-template-columns: repeat(4, 1fr) auto; + align-items: end; + } +} + +.finder-field { + display: flex; + flex-direction: column; +} + +.finder-label { + font-family: var(--font-display); + font-size: var(--text-sm); + font-weight: 700; + color: var(--color-neutral-700); + margin-bottom: var(--space-2); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.finder-input, +.finder-select { + background: var(--color-neutral-50); + border: 2px solid var(--color-neutral-200); + border-radius: var(--radius-lg); + padding: var(--space-4); + font-size: var(--text-base); + font-weight: 500; + color: var(--color-neutral-900); + transition: all var(--duration-normal) var(--ease-out); + font-family: var(--font-sans); +} + +.finder-input:focus, +.finder-select:focus { + outline: none; + border-color: var(--color-primary-500); + background: white; + box-shadow: 0 0 0 4px rgb(168 85 247 / 0.1); +} + +.finder-input::placeholder { + color: var(--color-neutral-500); +} + +.finder-select { + cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 12px center; + background-repeat: no-repeat; + background-size: 16px; + padding-right: var(--space-12); +} + +.price-range { + display: flex; + flex-direction: column; +} + +.price-range-label { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--space-3); +} + +.price-value { + font-family: var(--font-display); + font-weight: 800; + color: var(--color-primary-600); +} + +.price-slider { + width: 100%; + height: 6px; + border-radius: var(--radius-full); + background: var(--color-neutral-200); + outline: none; + appearance: none; + cursor: pointer; +} + +.price-slider::-webkit-slider-thumb { + appearance: none; + width: 24px; + height: 24px; + border-radius: var(--radius-full); + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + cursor: pointer; + border: 3px solid white; + box-shadow: var(--shadow-lg); +} + +.price-slider::-moz-range-thumb { + width: 24px; + height: 24px; + border-radius: var(--radius-full); + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + cursor: pointer; + border: 3px solid white; + box-shadow: var(--shadow-lg); +} + +.finder-search-btn { + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + color: white; + border: none; + border-radius: var(--radius-lg); + padding: var(--space-5) var(--space-8); + font-family: var(--font-display); + font-size: var(--text-lg); + font-weight: 800; + cursor: pointer; + transition: all var(--duration-normal) var(--ease-out); + box-shadow: var(--shadow-purple-md); + display: flex; + align-items: center; + gap: var(--space-2); + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; +} + +.finder-search-btn:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-purple-lg); +} + +.finder-search-btn:active { + transform: translateY(0); +} + +@media (max-width: 768px) { + .event-finder { + margin: var(--space-8) auto; + padding: var(--space-6); + } + + .finder-form { + gap: var(--space-4); + } + + .finder-search-btn { + justify-content: center; + padding: var(--space-4) var(--space-6); + font-size: var(--text-base); + } +} + +/* Enhanced animations */ +.animate-fadeInUp { + opacity: 0; + transform: translateY(30px); + transition: all 0.6s var(--ease-out); +} + +.animate-fadeInUp.visible { + opacity: 1; + transform: translateY(0); +} + +/* Loading spinner animation */ +@keyframes spin { + to { transform: rotate(360deg); } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/footer.css b/app/assets/stylesheets/components/footer.css new file mode 100755 index 0000000..7cb9f75 --- /dev/null +++ b/app/assets/stylesheets/components/footer.css @@ -0,0 +1,82 @@ + +.footer { + background: var(--color-neutral-800); + color: var(--color-neutral-300); + padding: var(--space-8) 0 var(--space-4); +} + +.footer-content { + display: grid; + gap: var(--space-6); + margin-bottom: var(--space-6); +} + +@media (min-width: 768px) { + .footer-content { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1024px) { + .footer-content { + grid-template-columns: repeat(4, 1fr); + } +} + +.footer-section h3 { + font-family: var(--font-display); + font-weight: 700; + margin-bottom: var(--space-3); + color: white; + font-size: var(--text-lg); +} + +.footer-links { + list-style: none; +} + +.footer-links li { + margin-bottom: var(--space-2); +} + +.footer-links a { + color: var(--color-neutral-400); + text-decoration: none; + transition: color var(--duration-normal); + font-size: var(--text-sm); +} + +.footer-links a:hover { + color: var(--color-accent-400); +} + +.footer-bottom { + border-top: 1px solid var(--color-neutral-700); + padding-top: var(--space-4); + text-align: center; + color: var(--color-neutral-400); + font-size: var(--text-sm); +} + +@media (max-width: 768px) { + .hero { + padding: var(--space-8) 0 var(--space-6); + } + + .cta-group { + flex-direction: column; + align-items: center; + } + + .hero-stats { + gap: var(--space-4); + } + + .features-grid { + grid-template-columns: 1fr; + } + + .stats-grid { + grid-template-columns: repeat(2, 1fr); + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/header.css b/app/assets/stylesheets/components/header.css new file mode 100755 index 0000000..e69de29 diff --git a/app/assets/stylesheets/components/hero.css b/app/assets/stylesheets/components/hero.css new file mode 100755 index 0000000..a48eb11 --- /dev/null +++ b/app/assets/stylesheets/components/hero.css @@ -0,0 +1,238 @@ +.hero { + background: linear-gradient(135deg, + rgba(168, 85, 247, 0.08) 0%, + rgba(236, 72, 153, 0.08) 50%, + rgba(168, 85, 247, 0.08) 100%); + padding: var(--space-12) 0 var(--space-8); + text-align: center; + position: relative; + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + radial-gradient(circle at 20% 80%, rgba(168, 85, 247, 0.1) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(236, 72, 153, 0.1) 0%, transparent 50%); + pointer-events: none; +} + +.hero-content { + position: relative; + z-index: 1; +} + +.hero h1 { + font-family: var(--font-display); + font-size: clamp(2rem, 5vw, 3.5rem); + font-weight: 900; + margin-bottom: var(--space-4); + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + line-height: 1.1; + letter-spacing: -0.02em; +} + +.hero .subtitle { + font-size: var(--text-lg); + color: var(--color-neutral-600); + margin-bottom: var(--space-6); + max-width: 700px; + margin-left: auto; + margin-right: auto; + font-weight: 500; +} + +.cta-group { + display: flex; + gap: var(--space-4); + justify-content: center; + flex-wrap: wrap; + margin-bottom: var(--space-8); +} + +.hero-stats { + display: flex; + justify-content: center; + gap: var(--space-6); + flex-wrap: wrap; +} + +.hero-stat { + text-align: center; +} + +.hero-stat-number { + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 800; + color: var(--color-primary-600); + display: block; +} + +.hero-stat-label { + font-size: var(--text-xs); + color: var(--color-neutral-500); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.section { + padding: var(--space-12) 0; +} + +.section-header { + text-align: center; + margin-bottom: var(--space-8); +} + +.section-title { + font-family: var(--font-display); + font-size: clamp(1.5rem, 3vw, 2.5rem); + font-weight: 800; + margin-bottom: var(--space-3); + color: var(--color-neutral-900); + line-height: 1.2; +} + +.section-description { + font-size: var(--text-lg); + color: var(--color-neutral-600); + max-width: 600px; + margin: 0 auto; +} + +.features-section { + background: linear-gradient(135deg, var(--color-primary-50) 0%, var(--color-accent-50) 100%); +} + +.features-grid { + display: grid; + gap: var(--space-6); +} + +@media (min-width: 768px) { + .features-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1024px) { + .features-grid { + grid-template-columns: repeat(4, 1fr); + } +} + +.feature-card { + background: white; + padding: var(--space-6); + border-radius: var(--radius-xl); + text-align: center; + box-shadow: var(--shadow-sm); + border: 1px solid var(--color-neutral-200); + transition: all var(--duration-normal) var(--ease-out); +} + +.feature-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.feature-icon { + width: 56px; + height: 56px; + background: linear-gradient(135deg, var(--color-primary-500) 0%, var(--color-accent-400) 100%); + color: white; + border-radius: var(--radius-xl); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto var(--space-3); + box-shadow: var(--shadow-purple-sm); +} + +.feature-title { + font-family: var(--font-display); + font-size: var(--text-lg); + font-weight: 700; + margin-bottom: var(--space-2); + color: var(--color-neutral-900); +} + +.feature-description { + color: var(--color-neutral-600); + line-height: 1.6; + font-size: var(--text-sm); +} + +.stats-section { + background: var(--color-neutral-900); + color: white; +} + +.stats-grid { + display: grid; + gap: var(--space-6); + text-align: center; +} + +@media (min-width: 768px) { + .stats-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1024px) { + .stats-grid { + grid-template-columns: repeat(4, 1fr); + } +} + +.stat-item { + padding: var(--space-4); +} + +.stat-number { + font-family: var(--font-display); + font-size: clamp(1.5rem, 4vw, 2.5rem); + font-weight: 900; + color: var(--color-accent-400); + display: block; + margin-bottom: var(--space-1); +} + +.stat-label { + color: var(--color-neutral-300); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: var(--text-xs); +} + +.cta-section { + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + color: white; + text-align: center; + padding: var(--space-12) 0; +} + +.cta-content h2 { + font-family: var(--font-display); + font-size: clamp(1.5rem, 3vw, 2rem); + font-weight: 800; + margin-bottom: var(--space-3); +} + +.cta-content p { + font-size: var(--text-lg); + margin-bottom: var(--space-6); + opacity: 0.9; +} \ No newline at end of file diff --git a/app/assets/stylesheets/pages/home.css b/app/assets/stylesheets/pages/home.css new file mode 100755 index 0000000..c66d87e --- /dev/null +++ b/app/assets/stylesheets/pages/home.css @@ -0,0 +1,171 @@ +/* Updated Featured Events Grid - 3 Cards Side by Side */ + .featured-events-grid { + display: grid; + gap: var(--space-8); + grid-template-columns: 1fr; + } + + @media (min-width: 768px) { + .featured-events-grid { + grid-template-columns: repeat(2, 1fr); + } + } + + @media (min-width: 1024px) { + .featured-events-grid { + grid-template-columns: repeat(3, 1fr); + } + } + + .featured-event-card { + background: white; + border-radius: var(--radius-xl); + overflow: hidden; + box-shadow: var(--shadow-md); + transition: all var(--duration-slow) var(--ease-out); + border: 1px solid var(--color-neutral-200); + position: relative; + } + + .featured-event-card:hover { + transform: translateY(-8px) scale(1.02); + box-shadow: var(--shadow-2xl); + border-color: var(--color-primary-200); + } + + .featured-event-image { + width: 100%; + height: 240px; + object-fit: cover; + transition: transform var(--duration-slow) var(--ease-out); + } + + .featured-event-card:hover .featured-event-image { + transform: scale(1.05); + } + + .featured-event-content { + padding: var(--space-6); + } + + .featured-event-badges { + display: flex; + gap: var(--space-2); + margin-bottom: var(--space-4); + flex-wrap: wrap; + } + + .featured-event-title { + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 700; + margin-bottom: var(--space-3); + color: var(--color-neutral-900); + line-height: 1.3; + } + + .featured-event-meta { + display: flex; + flex-direction: column; + gap: var(--space-2); + margin-bottom: var(--space-4); + } + + .featured-event-meta-item { + display: flex; + align-items: center; + gap: var(--space-2); + color: var(--color-neutral-600); + font-size: var(--text-sm); + font-weight: 500; + } + + .featured-event-description { + color: var(--color-neutral-700); + margin-bottom: var(--space-6); + line-height: 1.6; + font-size: var(--text-sm); + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .featured-event-footer { + display: flex; + justify-content: space-between; + align-items: center; + } + + .featured-event-price { + font-family: var(--font-display); + font-size: var(--text-xl); + font-weight: 800; + color: var(--color-primary-600); + } + + @media (max-width: 768px) { + .featured-event-image { + height: 200px; + } + + .featured-event-content { + padding: var(--space-4); + } + } + + /* Enhanced animations */ + .animate-slideInLeft { + opacity: 0; + transform: translateX(-30px); + transition: all 0.5s var(--ease-out); + } + + .animate-slideInLeft.visible { + opacity: 1; + transform: translateX(0); + } + + .animate-slideInRight { + opacity: 0; + transform: translateX(30px); + transition: all 0.5s var(--ease-out); + } + + .animate-slideInRight.visible { + opacity: 1; + transform: translateX(0); + } + + /* Added missing animation for fadeInUp */ + .animate-fadeInUp { + opacity: 0; + transform: translateY(30px); + transition: all 0.5s var(--ease-out); + } + + .animate-fadeInUp.visible { + opacity: 1; + transform: translateY(0); + } + + /* Feature Stats Styling */ + .feature-stat { + display: flex; + align-items: center; + gap: var(--space-2); + margin-top: var(--space-4); + } + + .stat-number { + font-family: var(--font-display); + font-size: var(--text-2xl); + font-weight: 700; + color: var(--color-primary-600); + } + + .stat-label { + font-size: var(--text-sm); + color: var(--color-neutral-600); + font-weight: 500; + } \ No newline at end of file diff --git a/app/assets/stylesheets/theme.css b/app/assets/stylesheets/theme.css index a15a5b9..3b9c25c 100755 --- a/app/assets/stylesheets/theme.css +++ b/app/assets/stylesheets/theme.css @@ -1,161 +1,790 @@ -/* Theme Rules from docs/theme-rules.md - Light Theme Only */ - -/* Custom properties for the design system */ :root { - /* Primary - Purple gradient system */ - --color-primary-50: #faf5ff; - --color-primary-100: #f3e8ff; - --color-primary-200: #e9d5ff; - --color-primary-300: #d8b4fe; - --color-primary-400: #c084fc; - --color-primary-500: #a855f7; - --color-primary-600: #9333ea; - --color-primary-700: #7e22ce; - --color-primary-800: #6b21a8; - --color-primary-900: #581c87; + /* Colors */ + --color-primary-50: #f5f3ff; + --color-primary-100: #ede9fe; + --color-primary-200: #ddd6fe; + --color-primary-300: #c4b5fd; + --color-primary-400: #a78bfa; + --color-primary-500: #8b5cf6; + --color-primary-600: #7c3aed; + --color-primary-700: #6d28d9; + --color-primary-800: #5b21b6; + --color-primary-900: #4c1d95; - /* Accent - Pink gradient */ - --color-accent-400: #f472b6; - --color-accent-500: #ec4899; - --color-accent-600: #db2777; + --color-accent-50: #fdf2f8; + --color-accent-100: #fce7f3; + --color-accent-200: #fbcfe8; + --color-accent-300: #f9a8d4; + --color-accent-400: #f472b6; + --color-accent-500: #ec4899; + --color-accent-600: #db2777; + --color-accent-700: #be185d; + --color-accent-800: #9d174d; + --color-accent-900: #831843; - /* Neutral - Slate system */ - --color-neutral-50: #f8fafc; - --color-neutral-100: #f1f5f9; - --color-neutral-200: #e2e8f0; - --color-neutral-300: #cbd5e1; - --color-neutral-400: #94a3b8; - --color-neutral-500: #64748b; - --color-neutral-600: #475569; - --color-neutral-700: #334155; - --color-neutral-800: #1e293b; - --color-neutral-900: #0f172a; + --color-neutral-50: #fafafa; + --color-neutral-100: #f5f5f5; + --color-neutral-200: #e5e5e5; + --color-neutral-300: #d4d4d4; + --color-neutral-400: #a3a3a3; + --color-neutral-500: #737373; + --color-neutral-600: #525252; + --color-neutral-700: #404040; + --color-neutral-800: #262626; + --color-neutral-900: #171717; - /* Font families */ - --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --color-success: #10b981; + --color-success-light: #d1fae5; + --color-success-dark: #047857; - /* Spacing scale */ - --space-1: 0.25rem; /* 4px */ - --space-2: 0.5rem; /* 8px */ - --space-3: 0.75rem; /* 12px */ - --space-4: 1rem; /* 16px */ - --space-5: 1.25rem; /* 20px */ - --space-6: 1.5rem; /* 24px */ - --space-8: 2rem; /* 32px */ - --space-10: 2.5rem; /* 40px */ - --space-12: 3rem; /* 48px */ + --color-warning: #f59e0b; + --color-warning-light: #fef3c7; + --color-warning-dark: #b45309; + + --color-danger: #ef4444; + --color-danger-light: #fee2e2; + --color-danger-dark: #b91c1c; + + /* Typography */ + --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + --font-display: 'Outfit', var(--font-sans); + + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.875rem; /* 30px */ + --text-4xl: 2.25rem; /* 36px */ + --text-5xl: 3rem; /* 48px */ + + /* Spacing */ + --space-1: 0.25rem; /* 4px */ + --space-2: 0.5rem; /* 8px */ + --space-3: 0.75rem; /* 12px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-8: 2rem; /* 32px */ + --space-10: 2.5rem; /* 40px */ + --space-12: 3rem; /* 48px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + + /* Radius */ + --radius: 0.25rem; /* 4px */ + --radius-md: 0.5rem; /* 8px */ + --radius-lg: 0.75rem; /* 12px */ + --radius-xl: 1rem; /* 16px */ + --radius-2xl: 1.5rem; /* 24px */ + --radius-full: 9999px; + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + + --shadow-purple-sm: 0 1px 3px 0 rgba(168, 85, 247, 0.1), 0 1px 2px 0 rgba(168, 85, 247, 0.06); + --shadow-purple-md: 0 4px 6px -1px rgba(168, 85, 247, 0.1), 0 2px 4px -1px rgba(168, 85, 247, 0.06); + --shadow-purple-lg: 0 10px 15px -3px rgba(168, 85, 247, 0.1), 0 4px 6px -2px rgba(168, 85, 247, 0.05); + + /* Transitions */ + --duration-fast: 150ms; + --duration-normal: 300ms; + --duration-slow: 500ms; + + --ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +/* Base styles */ + +body { + font-family: var(--font-sans); + line-height: 1.6; + color: var(--color-neutral-900); + background: var(--color-neutral-50); +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--space-4); +} + +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-display); + font-weight: 700; + line-height: 1.2; + margin-bottom: var(--space-2); +} + +h1 { + font-size: var(--text-4xl); + font-weight: 900; +} + +h2 { + font-size: var(--text-3xl); + font-weight: 800; +} + +h3 { + font-size: var(--text-2xl); + font-weight: 700; +} + +h4 { + font-size: var(--text-xl); + font-weight: 700; +} + +p { + margin-bottom: var(--space-4); +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--space-2); + font-family: var(--font-display); + font-weight: 700; + border: none; + cursor: pointer; + border-radius: var(--radius-lg); + transition: all var(--duration-normal) var(--ease-out); + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; +} + +.btn-sm { + padding: var(--space-2) var(--space-4); + font-size: var(--text-sm); +} + +.btn-md { + padding: var(--space-3) var(--space-6); + font-size: var(--text-base); +} + +.btn-lg { + padding: var(--space-4) var(--space-8); + font-size: var(--text-lg); } -/* Button components */ .btn-primary { - @apply bg-gradient-to-r from-purple-600 to-pink-600 text-white font-medium py-2 px-4 rounded-lg shadow-sm hover:shadow-md transition-all duration-200; + background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-500) 100%); + color: white; + box-shadow: var(--shadow-purple-md); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-purple-lg); } .btn-secondary { - @apply bg-white text-purple-600 border border-purple-200 font-medium py-2 px-4 rounded-lg hover:bg-purple-50 transition-colors duration-200; + background: var(--color-neutral-800); + color: white; } -.btn-destructive { - @apply bg-red-600 text-white font-medium py-2 px-4 rounded-lg shadow-sm hover:bg-red-700 transition-colors duration-200; +.btn-secondary:hover { + background: var(--color-neutral-900); + transform: translateY(-2px); } -/* Card components */ -.card { - @apply bg-white rounded-lg shadow-sm border border-neutral-200 p-6 hover:shadow-md transition-shadow duration-200; +.btn-outline { + background: transparent; + border: 2px solid var(--color-primary-600); + color: var(--color-primary-600); } -.card-header { - @apply pb-4 border-b border-neutral-200 mb-4; +.btn-outline:hover { + background: var(--color-primary-50); + transform: translateY(-2px); } -.card-body { - @apply space-y-4; +.btn-ghost { + background: transparent; + color: var(--color-neutral-700); } -/* Form components */ -.form-input { - @apply block w-full rounded-md border-neutral-300 shadow-sm focus:border-purple-500 focus:ring-purple-500 sm:text-sm; +.btn-ghost:hover { + background: var(--color-neutral-100); +} + +/* Form elements */ +.form-group { + margin-bottom: var(--space-4); } .form-label { - @apply block text-sm font-medium text-neutral-700 mb-1; + font-family: var(--font-display); + font-size: var(--text-sm); + font-weight: 700; + color: var(--color-neutral-700); + margin-bottom: var(--space-2); + text-transform: uppercase; + letter-spacing: 0.05em; + display: block; +} + +.form-input, +.form-select, +.form-textarea { + background: var(--color-neutral-50); + border: 2px solid var(--color-neutral-200); + border-radius: var(--radius-lg); + padding: var(--space-4); + font-size: var(--text-base); + font-weight: 500; + color: var(--color-neutral-900); + transition: all var(--duration-normal) var(--ease-out); + font-family: var(--font-sans); + width: 100%; +} + +.form-input:focus, +.form-select:focus, +.form-textarea:focus { + outline: none; + border-color: var(--color-primary-500); + background: white; + box-shadow: 0 0 0 4px rgba(168, 85, 247, 0.1); +} + +.form-input::placeholder { + color: var(--color-neutral-500); +} + +.form-select { + cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 12px center; + background-repeat: no-repeat; + background-size: 16px; + padding-right: var(--space-12); +} + +.form-textarea { + min-height: 100px; + resize: vertical; } .form-error { - @apply text-sm text-red-600 mt-1; -} - -/* Navigation */ -.nav-link { - @apply text-neutral-600 hover:text-purple-600 px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200; -} - -.nav-link-active { - @apply text-purple-600 bg-purple-50; -} - -/* Layout utilities */ -.container { - @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; -} - -.grid-responsive { - @apply grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6; -} - -.grid-cards { - @apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6; -} - -/* Animation utilities */ -.hover-lift { - @apply transition-transform duration-200 hover:-translate-y-1; -} - -.hover-glow { - @apply transition-all duration-200 hover:shadow-lg hover:shadow-purple-500/25; + color: var(--color-danger); + font-size: var(--text-sm); + margin-top: var(--space-1); + display: flex; + align-items: center; + gap: var(--space-1); } .focus-ring { - @apply focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2; + transition: all var(--duration-normal) var(--ease-out); } -.transition-fast { - @apply transition-all duration-150 ease-in-out; +.focus-ring:focus { + outline: none; + border-color: var(--color-primary-500); + background: white; + box-shadow: 0 0 0 4px rgba(168, 85, 247, 0.1); } -.transition-normal { - @apply transition-all duration-200 ease-in-out; +/* Badges */ +.badge { + display: inline-flex; + align-items: center; + padding: var(--space-1) var(--space-3); + border-radius: var(--radius-full); + font-size: var(--text-xs); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; } -.transition-slow { - @apply transition-all duration-300 ease-in-out; +.badge-available { + background: var(--color-success-light); + color: var(--color-success-dark); + border: 1px solid var(--color-success); } -/* State utilities */ -.disabled { - @apply opacity-50 cursor-not-allowed; +.badge-limited { + background: var(--color-warning-light); + color: var(--color-warning-dark); + border: 1px solid var(--color-warning); } -.animate-pulse-subtle { - @apply animate-pulse; - animation-duration: 3s; +.badge-sold-out { + background: var(--color-danger-light); + color: var(--color-danger-dark); + border: 1px solid var(--color-danger); } -.fade-in { - @apply animate-in fade-in-0 duration-500; +.badge-featured { + background: var(--color-accent-100); + color: var(--color-accent-700); + border: 1px solid var(--color-accent-300); } -/* Accessibility utilities */ -.focus-visible { - @apply focus:outline-none focus-visible:ring-2 focus-visible:ring-purple-500 focus-visible:ring-offset-2; +.badge-vip { + background: var(--color-primary-100); + color: var(--color-primary-800); + border: 1px solid var(--color-primary-300); } -.text-primary { - @apply text-neutral-900; +/* Cards */ +.card { + background: white; + border-radius: var(--radius-xl); + padding: var(--space-6); + border: 1px solid var(--color-neutral-200); + box-shadow: var(--shadow-sm); + transition: all var(--duration-slow) var(--ease-out); +} + +.card.hover-lift:hover { + transform: translateY(-8px) scale(1.02); + box-shadow: var(--shadow-2xl); + border-color: var(--color-primary-200); +} + +.card-header { + margin-bottom: var(--space-4); +} + +.card-body { + margin-bottom: var(--space-4); +} + +.event-card { + background: white; + border-radius: var(--radius-xl); + overflow: hidden; + box-shadow: var(--shadow-md); + transition: all var(--duration-slow) var(--ease-out); + border: 1px solid var(--color-neutral-200); + position: relative; +} + +.event-card.hover-glow:hover { + transform: translateY(-8px) scale(1.02); + box-shadow: var(--shadow-2xl); + border-color: var(--color-primary-200); +} + +/* Navigation */ +.nav { + background: white; + box-shadow: var(--shadow-sm); + position: sticky; + top: 0; + z-index: 100; +} + +.nav-link { + color: var(--color-neutral-700); + text-decoration: none; + font-weight: 600; + padding: var(--space-3) var(--space-2); + border-radius: var(--radius); + transition: all var(--duration-normal); +} + +.nav-link:hover { + color: var(--color-primary-600); + background: var(--color-primary-50); +} + +/* Search box */ +.search-box { + position: relative; + max-width: 400px; +} + +.search-input { + padding-left: var(--space-12); +} + +.search-icon { + position: absolute; + left: var(--space-4); + top: 50%; + transform: translateY(-50%); + color: var(--color-neutral-400); +} + +/* Price tags */ +.price-tag { + display: inline-flex; + align-items: center; + gap: var(--space-1); + font-family: var(--font-display); + font-weight: 800; +} + +.price-tag-sm { + font-size: var(--text-sm); +} + +.price-tag-md { + font-size: var(--text-lg); +} + +.price-tag-lg { + font-size: var(--text-2xl); +} + +/* Avatars */ +.avatar-group { + display: flex; + align-items: center; + gap: var(--space-2); +} + +.avatar { + width: 40px; + height: 40px; + border-radius: var(--radius-full); + background: var(--color-primary-100); + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + color: var(--color-primary-600); + border: 2px solid white; + box-shadow: var(--shadow-sm); + position: relative; +} + +.avatar-lg { + width: 60px; + height: 60px; +} + +.avatar-sm { + width: 32px; + height: 32px; + font-size: var(--text-xs); +} + +.avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: var(--radius-full); +} + +.avatar-status { + position: absolute; + bottom: 0; + right: 0; + width: 12px; + height: 12px; + border-radius: var(--radius-full); + border: 2px solid white; +} + +.status-online { background: var(--color-success); } +.status-offline { background: var(--color-neutral-400); } +.status-busy { background: var(--color-danger); } + +/* Progress bars */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--color-neutral-200); + border-radius: var(--radius-full); + overflow: hidden; + margin-bottom: var(--space-3); +} + +.progress-fill { + height: 100%; + background: linear-gradient(135deg, var(--color-primary-500) 0%, var(--color-accent-400) 100%); + border-radius: var(--radius-full); + transition: width var(--duration-slow) var(--ease-out); +} + +/* Rating stars */ +.rating { + display: flex; + gap: var(--space-1); + align-items: center; +} + +.rating-star { + width: 16px; + height: 16px; + color: var(--color-warning); +} + +.rating-star.filled { + fill: currentColor; +} + +.rating-star.empty { + fill: none; + stroke: currentColor; + stroke-width: 2; +} + +/* Notifications */ +.notification { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-4); + border-radius: var(--radius-lg); + margin-bottom: var(--space-3); + font-weight: 500; +} + +.notification-success { + background: var(--color-success-light); + color: var(--color-success-dark); + border: 1px solid var(--color-success); +} + +.notification-warning { + background: var(--color-warning-light); + color: var(--color-warning-dark); + border: 1px solid var(--color-warning); +} + +.notification-error { + background: var(--color-danger-light); + color: var(--color-danger-dark); + border: 1px solid var(--color-danger); +} + +.notification-info { + background: var(--color-primary-50); + color: var(--color-primary-800); + border: 1px solid var(--color-primary-200); +} + +/* Tabs */ +.tabs { + border-bottom: 1px solid var(--color-neutral-200); + margin-bottom: var(--space-6); +} + +.tab-list { + display: flex; + gap: var(--space-1); +} + +.tab-button { + background: none; + border: none; + padding: var(--space-3) var(--space-4); + font-weight: 500; + color: var(--color-neutral-600); + cursor: pointer; + border-bottom: 2px solid transparent; + transition: all var(--duration-normal); + font-family: var(--font-display); +} + +.tab-button:hover { + color: var(--color-primary-600); + background: var(--color-primary-50); + border-radius: var(--radius-md) var(--radius-md) 0 0; +} + +.tab-button.active { + color: var(--color-primary-600); + border-bottom-color: var(--color-primary-600); +} + +.tab-content { + display: none; + padding: var(--space-4) 0; +} + +.tab-content.active { + display: block; +} + +/* Tooltips */ +.tooltip { + position: relative; + display: inline-block; +} + +.tooltip-content { + position: absolute; + bottom: 125%; + left: 50%; + transform: translateX(-50%); + background: var(--color-neutral-900); + color: white; + padding: var(--space-2) var(--space-3); + border-radius: var(--radius); + font-size: var(--text-sm); + white-space: nowrap; + opacity: 0; + visibility: hidden; + transition: all var(--duration-normal); + z-index: 100; +} + +.tooltip-content::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 5px solid transparent; + border-top-color: var(--color-neutral-900); +} + +.tooltip:hover .tooltip-content { + opacity: 1; + visibility: visible; +} + +/* Loading states */ +.loading-spinner { + width: 40px; + height: 40px; + border: 3px solid var(--color-neutral-200); + border-top-color: var(--color-primary-600); + border-radius: var(--radius-full); + animation: spin 1s linear infinite; +} + +.loading-dots { + display: flex; + gap: var(--space-1); +} + +.loading-dot { + width: 8px; + height: 8px; + background: var(--color-primary-600); + border-radius: var(--radius-full); + animation: bounce 1.4s infinite both; +} + +.loading-dot:nth-child(2) { animation-delay: 0.2s; } +.loading-dot:nth-child(3) { animation-delay: 0.4s; } + +@keyframes spin { + to { transform: rotate(360deg); } +} + +@keyframes bounce { + 0%, 80%, 100% { transform: scale(0); } + 40% { transform: scale(1); } +} + +/* Breadcrumbs */ +.breadcrumb { + display: flex; + align-items: center; + gap: var(--space-2); + font-size: var(--text-sm); +} + +.breadcrumb-item { + color: var(--color-neutral-600); + text-decoration: none; +} + +.breadcrumb-item:hover { + color: var(--color-primary-600); +} + +.breadcrumb-item.current { + color: var(--color-neutral-900); + font-weight: 600; +} + +.breadcrumb-separator { + color: var(--color-neutral-400); +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .container { + padding: 0 var(--space-3); + } + + h1 { + font-size: var(--text-3xl); + } + + h2 { + font-size: var(--text-2xl); + } + + .btn-lg { + padding: var(--space-3) var(--space-6); + font-size: var(--text-base); + } + + .btn-md { + padding: var(--space-2) var(--space-4); + font-size: var(--text-sm); + } + + .form-input, + .form-select, + .form-textarea { + padding: var(--space-3); + } +} + +/* Additional styles for enhanced Aperonight design */ +.event-card.hover-glow:hover { + transform: translateY(-8px) scale(1.02); + box-shadow: var(--shadow-2xl); + border-color: var(--color-primary-200); +} + +.price-tag { + display: inline-flex; + align-items: center; + gap: var(--space-1); + font-family: var(--font-display); + font-weight: 800; +} + +.price-tag-sm { + font-size: var(--text-sm); +} + +.price-tag-md { + font-size: var(--text-lg); +} + +.price-tag-lg { + font-size: var(--text-2xl); +} + +.search-box { + position: relative; + width: 100%; +} + +.search-input { + padding-left: var(--space-12); +} + +.search-icon { + position: absolute; + left: var(--space-4); + top: 50%; + transform: translateY(-50%); + color: var(--color-neutral-400); } -.text-secondary { - @apply text-neutral-600; -} \ No newline at end of file diff --git a/app/controllers/api/v1/events_controller.rb b/app/controllers/api/v1/events_controller.rb new file mode 100755 index 0000000..1469a1a --- /dev/null +++ b/app/controllers/api/v1/events_controller.rb @@ -0,0 +1,83 @@ +# Contrôleur API pour la gestion des ressources d'événements +# Fournit des points de terminaison RESTful pour les opérations CRUD sur le modèle Event + +module Api + module V1 + class EventsController < ApiController + # Charge l'évén avant certaines actions pour réduire les duplications + before_action :set_event, only: [ :show, :update, :destroy ] + + # GET /api/v1/events + # Récupère tous les événements triés par date de création (du plus récent au plus ancien) + def index + @events = Event.all.order(created_at: :desc) + render json: @events, status: :ok + end + + # GET /api/v1/events/:id + # Récupère un seul événement par son ID + # Retourne 404 si l'événement n'est pas trouvé + def show + render json: @event, status: :ok + end + + # POST /api/v1/events + # Crée un nouvel événement avec les attributs fournis + # Retourne 201 Created en cas de succès avec les données de l'événement + # Retourne 422 Unprocessable Entity avec les messages d'erreur en cas d'échec + def create + @event = Event.new(event_params) + if @event.save + render json: @event, status: :created + else + render json: { errors: @event.errors.full_messages }, status: :unprocessable_entity + end + end + + # PATCH/PUT /api/v1/events/:id + # Met à jour un événement existant avec les attributs fournis + # Retourne 200 OK avec les données mises à jour en cas de succès + # Retourne 422 Unprocessable Entity avec les messages d'erreur en cas d'échec + def update + if @event.update(event_params) + render json: @event, status: :ok + else + render json: { errors: @event.errors.full_messages }, status: :unprocessable_entity + end + end + + # DELETE /api/v1/events/:id + # Supprime définitivement un événement + # Retourne 204 No Content en cas de succès + def destroy + @event.destroy + head :no_content + end + + private + + # Trouve un événement par son ID ou retourne 404 Introuvable + # Utilisé comme before_action pour les actions show, update et destroy + def set_event + @event = Event.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render json: { error: "Événement non trouvé" }, status: :not_found + end + + # Paramètres forts pour la création et la mise à jour des événements + # Liste blanche des attributs autorisés pour éviter les vulnérabilités de mass assignment + def event_params + params.require(:event).permit( + :name, + :description, + :state, + :venue_name, + :venue_address, + :latitude, + :longitude, + :featured + ) + end + end + end +end diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb deleted file mode 100755 index c1fd10a..0000000 --- a/app/controllers/api/v1/parties_controller.rb +++ /dev/null @@ -1,82 +0,0 @@ -# API controller for managing party resources -# Provides RESTful endpoints for CRUD operations on Party model -module Api - module V1 - class PartiesController < ApiController - # Load party before specific actions to reduce duplication - before_action :set_party, only: [ :show, :update, :destroy ] - - # GET /api/v1/parties - # Returns all parties sorted by creation date (newest first) - def index - @parties = Party.all.order(created_at: :desc) - render json: @parties, status: :ok - end - - # GET /api/v1/parties/:id - # Returns a single party by ID - # Returns 404 if party is not found - def show - render json: @party, status: :ok - end - - # POST /api/v1/parties - # Creates a new party with provided attributes - # Returns 201 Created on success with party data - # Returns 422 Unprocessable Entity with validation errors on failure - def create - @party = Party.new(party_params) - if @party.save - render json: @party, status: :created - else - render json: { errors: @party.errors.full_messages }, status: :unprocessable_entity - end - end - - # PATCH/PUT /api/v1/parties/:id - # Updates an existing party with provided attributes - # Returns 200 OK with updated party data on success - # Returns 422 Unprocessable Entity with validation errors on failure - def update - if @party.update(party_params) - render json: @party, status: :ok - else - render json: { errors: @party.errors.full_messages }, status: :unprocessable_entity - end - end - - # DELETE /api/v1/parties/:id - # Permanently deletes a party - # Returns 204 No Content on success - def destroy - @party.destroy - head :no_content - end - - private - - # Finds a party by ID or returns 404 Not Found - # Used as before_action for show, update, and destroy actions - def set_party - @party = Party.find(params[:id]) - rescue ActiveRecord::RecordNotFound - render json: { error: "Party not found" }, status: :not_found - end - - # Strong parameters for party creation and updates - # Whitelists permitted attributes to prevent mass assignment vulnerabilities - def party_params - params.require(:party).permit( - :name, - :description, - :state, - :venue_name, - :venue_address, - :latitude, - :longitude, - :featured - ) - end - end - end -end diff --git a/app/controllers/parties_controller.rb b/app/controllers/events_controller.rb similarity index 68% rename from app/controllers/parties_controller.rb rename to app/controllers/events_controller.rb index e4cbfd1..b3fbdfd 100755 --- a/app/controllers/parties_controller.rb +++ b/app/controllers/events_controller.rb @@ -1,22 +1,22 @@ -class PartiesController < ApplicationController +class EventsController < ApplicationController # Display all events def index - @parties = Party.includes(:user).upcoming.page(params[:page]).per(1) - # @parties = Party.page(params[:page]).per(12) + @events = Event.includes(:user).upcoming.page(params[:page]).per(1) + # @events = Event.page(params[:page]).per(12) end # Display desired event def show - @party = Party.find(params[:id]) + @event = Event.find(params[:id]) end # Handle checkout process def checkout - @party = Party.find(params[:id]) + @event = Event.find(params[:id]) cart_data = JSON.parse(params[:cart] || "{}") if cart_data.empty? - redirect_to party_path(@party), alert: "Please select at least one ticket" + redirect_to event_path(@event), alert: "Please select at least one ticket" return end @@ -25,7 +25,7 @@ class PartiesController < ApplicationController total_amount = 0 cart_data.each do |ticket_type_id, item| - ticket_type = @party.ticket_types.find_by(id: ticket_type_id) + ticket_type = @event.ticket_types.find_by(id: ticket_type_id) next unless ticket_type quantity = item["quantity"].to_i @@ -34,7 +34,7 @@ class PartiesController < ApplicationController # Check availability available = ticket_type.quantity - ticket_type.tickets.count if quantity > available - redirect_to party_path(@party), alert: "Not enough tickets available for #{ticket_type.name}" + redirect_to event_path(@event), alert: "Not enough tickets available for #{ticket_type.name}" return end @@ -48,7 +48,7 @@ class PartiesController < ApplicationController end if order_items.empty? - redirect_to party_path(@party), alert: "Invalid order" + redirect_to event_path(@event), alert: "Invalid order" return end @@ -59,6 +59,6 @@ class PartiesController < ApplicationController # For now, we'll just redirect with a success message # In a real app, you'd redirect to a payment page - redirect_to party_path(@party), notice: "Order created successfully! Proceeding to payment..." + redirect_to event_path(@event), notice: "Order created successfully! Proceeding to payment..." end end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 4a4948c..88b1c89 100755 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -5,10 +5,10 @@ class PagesController < ApplicationController # skip_before_action :authenticate_user!, only: [ :home ] before_action :authenticate_user!, only: [ :dashboard ] - # Homepage showing featured parties + # Homepage showing featured events def home - # @parties = Party.published.featured.limit(3) - @parties = Party.where(state: :published).order(created_at: :desc) + # @events = Event.published.featured.limit(3) + # @events = Event.where(state: :published).order(created_at: :desc) if user_signed_in? return redirect_to(dashboard_path) @@ -18,15 +18,15 @@ class PagesController < ApplicationController # User dashboard showing personalized content # Accessible only to authenticated users def dashboard - @available_parties = Party.published.count - @events_this_week = Party.published.where("start_time BETWEEN ? AND ?", Date.current.beginning_of_week, Date.current.end_of_week).count - @today_parties = Party.published.where("DATE(start_time) = ?", Date.current).order(start_time: :asc) - @tomorrow_parties = Party.published.where("DATE(start_time) = ?", Date.current + 1).order(start_time: :asc) - @other_parties = Party.published.upcoming.where.not("DATE(start_time) IN (?)", [Date.current, Date.current + 1]).order(start_time: :asc).page(params[:page]) + @available_events = Event.published.count + @events_this_week = Event.published.where("start_time BETWEEN ? AND ?", Date.current.beginning_of_week, Date.current.end_of_week).count + @today_events = Event.published.where("DATE(start_time) = ?", Date.current).order(start_time: :asc) + @tomorrow_events = Event.published.where("DATE(start_time) = ?", Date.current + 1).order(start_time: :asc) + @other_events = Event.published.upcoming.where.not("DATE(start_time) IN (?)", [Date.current, Date.current + 1]).order(start_time: :asc).page(params[:page]) end - # Events page showing all published parties with pagination + # Events page showing all published events with pagination def events - @parties = Party.published.order(created_at: :desc).page(params[:page]) + @events = Event.published.order(created_at: :desc).page(params[:page]) end end diff --git a/app/javascript/controllers/counter_controller.js b/app/javascript/controllers/counter_controller.js index 129be1d..b463eec 100755 --- a/app/javascript/controllers/counter_controller.js +++ b/app/javascript/controllers/counter_controller.js @@ -1,9 +1,9 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { - static values = { - target: Number, - decimal: Boolean, + static values = { + target: { type: Number, default: 0 }, + decimal: { type: Boolean, default: false }, duration: { type: Number, default: 2000 } } @@ -27,35 +27,44 @@ export default class extends Controller { } animate() { - const startValue = 0 - const startTime = performance.now() + // Find the target element with data-target-value + const targetElement = this.element.querySelector('.stat-number'); + if (!targetElement) return; + + // Get the target value + this.targetValue = parseInt(targetElement.getAttribute('data-target-value'), 10) || this.targetValue; + + const startValue = 0; + const startTime = performance.now(); const updateCounter = (currentTime) => { - const elapsedTime = currentTime - startTime - const progress = Math.min(elapsedTime / this.durationValue, 1) - + const elapsedTime = currentTime - startTime; + const progress = Math.min(elapsedTime / this.durationValue, 1); + // Easing function for smooth animation - const easeOutQuart = 1 - Math.pow(1 - progress, 4) - - let currentValue = startValue + (this.targetValue - startValue) * easeOutQuart - + const easeOutQuart = 1 - Math.pow(1 - progress, 4); + + let currentValue = startValue + (this.targetValue - startValue) * easeOutQuart; + if (this.decimalValue && this.targetValue < 10) { - currentValue = currentValue.toFixed(1) + currentValue = currentValue.toFixed(1); } else { - currentValue = Math.floor(currentValue) + currentValue = Math.floor(currentValue); } - this.element.textContent = currentValue + // Update only the text content of the target element + targetElement.textContent = currentValue; if (progress < 1) { - requestAnimationFrame(updateCounter) + requestAnimationFrame(updateCounter); } else { - this.element.textContent = this.decimalValue && this.targetValue < 10 - ? this.targetValue.toFixed(1) - : this.targetValue + const finalValue = this.decimalValue && this.targetValue < 10 + ? this.targetValue.toFixed(1) + : this.targetValue; + targetElement.textContent = finalValue; } } - requestAnimationFrame(updateCounter) + requestAnimationFrame(updateCounter); } } diff --git a/app/javascript/controllers/featured_event_controller.js b/app/javascript/controllers/featured_event_controller.js new file mode 100755 index 0000000..8d73a81 --- /dev/null +++ b/app/javascript/controllers/featured_event_controller.js @@ -0,0 +1,86 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["card"] + static classes = ["visible"] + static values = { + threshold: { type: Number, default: 0.1 }, + rootMargin: { type: String, default: '0px 0px -50px 0px' }, + staggerDelay: { type: Number, default: 0.2 } + } + + connect() { + console.log("FeaturedEventController connected") + this.setupIntersectionObserver() + this.setupStaggeredAnimations() + } + + disconnect() { + if (this.observer) { + this.observer.disconnect() + } + } + + setupIntersectionObserver() { + const observerOptions = { + threshold: this.thresholdValue, + rootMargin: this.rootMarginValue + } + + this.observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('visible') + } + }) + }, observerOptions) + + // Observe all card elements within this controller's scope + const elements = this.cardTargets + console.log("Card targets:", elements) + elements.forEach(el => { + this.observer.observe(el) + }) + } + + setupStaggeredAnimations() { + console.log("Setting up staggered animations") + console.log("Card targets:", this.cardTargets) + // Add staggered animation delays to cards + this.cardTargets.forEach((card, index) => { + card.style.transitionDelay = `${index * this.staggerDelayValue}s` + card.classList.remove('visible') + }) + } +} + + +/** Old code + + */ \ No newline at end of file diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 8847531..b3c7a46 100755 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -5,12 +5,15 @@ import { application } from "./application" import LogoutController from "./logout_controller" -import FlashMessage from "./flash_message_controller" +import FlashMessageController from "./flash_message_controller" import CounterController from "./counter_controller" +import FeaturedEventController from "./featured_event_controller" + import ShadcnTestController from "./shadcn_test_controller" application.register("logout", LogoutController) // Allow logout using js -application.register("flash-message", FlashMessage) // Dismiss notification after 5 secondes +application.register("flash-message", FlashMessageController) // Dismiss notification after 5 secondes application.register("counter", CounterController) // Simple counter for homepage +application.register("featured-event", FeaturedEventController) // Featured event controller for homepage application.register("shadcn-test", ShadcnTestController) // Test controller for Shadcn diff --git a/app/javascript/controllers/ticket_cart_controller.js b/app/javascript/controllers/ticket_cart_controller.js index 2e8f13d..08d8edf 100755 --- a/app/javascript/controllers/ticket_cart_controller.js +++ b/app/javascript/controllers/ticket_cart_controller.js @@ -2,7 +2,7 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["quantity", "cartCount", "cartTotal", "checkoutButton"] - static values = { partyId: String } + static values = { eventId: String } connect() { this.cart = {} @@ -78,7 +78,7 @@ export default class extends Controller { const form = document.createElement('form') form.method = 'POST' - form.action = `/parties/${this.partyIdValue}/checkout` + form.action = `/events/${this.eventIdValue}/checkout` form.style.display = 'none' // Add CSRF token diff --git a/app/models/party.rb b/app/models/event.rb similarity index 72% rename from app/models/party.rb rename to app/models/event.rb index f772689..6504dd4 100755 --- a/app/models/party.rb +++ b/app/models/event.rb @@ -1,11 +1,11 @@ -# Party model representing nightlife events and parties +# Event model representing nightlife events and events # Manages event details, location data, and publication state -class Party < ApplicationRecord - # Define states for party lifecycle management - # draft: Initial state when party is being created - # published: Party is visible to public and can be discovered - # canceled: Party has been canceled by organizer - # sold_out: Party has reached capacity and tickets are no longer available +class Event < ApplicationRecord + # Define states for Event lifecycle management + # draft: Initial state when Event is being created + # published: Event is visible to public and can be discovered + # canceled: Event has been canceled by organizer + # sold_out: Event has reached capacity and tickets are no longer available enum :state, { draft: 0, published: 1, @@ -18,7 +18,7 @@ class Party < ApplicationRecord has_many :ticket_types, dependent: :destroy has_many :tickets, through: :ticket_types - # Validations for party attributes + # Validations for Event attributes # Basic information validates :name, presence: true, length: { minimum: 3, maximum: 100 } validates :slug, presence: true, length: { minimum: 3, maximum: 100 } @@ -40,12 +40,12 @@ class Party < ApplicationRecord less_than_or_equal_to: 180 } - # Scopes for querying parties with common filters - scope :featured, -> { where(featured: true) } # Get featured parties for homepage - scope :published, -> { where(state: :published) } # Get publicly visible parties + # Scopes for querying events with common filters + scope :featured, -> { where(featured: true) } # Get featured events for homepage + scope :published, -> { where(state: :published) } # Get publicly visible events scope :search_by_name, ->(query) { where("name ILIKE ?", "%#{query}%") } # Search by name (case-insensitive) - # Scope for published parties ordered by start time + # Scope for published events ordered by start time scope :upcoming, -> { published.where("start_time >= ?", Time.current).order(start_time: :asc) } end diff --git a/app/models/ticket.rb b/app/models/ticket.rb index 9bb6419..2786194 100755 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -2,7 +2,7 @@ class Ticket < ApplicationRecord # Associations belongs_to :user belongs_to :ticket_type - has_one :party, through: :ticket_type + has_one :event, through: :ticket_type # Validations validates :qr_code, presence: true, uniqueness: true diff --git a/app/models/ticket_type.rb b/app/models/ticket_type.rb index bd3c79c..2d43d75 100755 --- a/app/models/ticket_type.rb +++ b/app/models/ticket_type.rb @@ -1,6 +1,6 @@ class TicketType < ApplicationRecord # Associations - belongs_to :party + belongs_to :event has_many :tickets, dependent: :destroy # Validations @@ -8,12 +8,13 @@ class TicketType < ApplicationRecord validates :description, presence: true, length: { minimum: 10, maximum: 500 } validates :price_cents, presence: true, numericality: { greater_than: 0 } validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 } - validates :party_id, presence: true validates :sale_start_at, presence: true validates :sale_end_at, presence: true validate :sale_end_after_start validates :requires_id, inclusion: { in: [ true, false ] } validates :minimum_age, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 120 }, allow_nil: true + validates :event_id, presence: true + private diff --git a/app/models/user.rb b/app/models/user.rb index c2caaee..f7de47c 100755 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -20,7 +20,7 @@ class User < ApplicationRecord :recoverable, :rememberable, :validatable # Relationships - has_many :parties, dependent: :destroy + has_many :events, dependent: :destroy has_many :tickets, dependent: :destroy # Validations diff --git a/app/views/components/_event_finder.html.erb b/app/views/components/_event_finder.html.erb new file mode 100755 index 0000000..79639f8 --- /dev/null +++ b/app/views/components/_event_finder.html.erb @@ -0,0 +1,157 @@ + +
+
+
+
+

Find Your Perfect Event

+

Discover afterwork events tailored to your preferences

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+
+
+
+ + + + \ No newline at end of file diff --git a/app/views/components/_party_item.html.erb b/app/views/components/_event_item.html.erb similarity index 71% rename from app/views/components/_party_item.html.erb rename to app/views/components/_event_item.html.erb index ddfbaa1..a3dd4a2 100755 --- a/app/views/components/_party_item.html.erb +++ b/app/views/components/_event_item.html.erb @@ -1,14 +1,14 @@ -<%= link_to party_path(party.slug, party), class: "group block p-4 rounded-lg border border-slate-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 hover:shadow-md transition-all duration-200" do %> +<%= link_to event_path(event.slug, event), class: "group block p-4 rounded-lg border border-slate-200 dark:border-slate-700 hover:border-purple-300 dark:hover:border-purple-600 hover:shadow-md transition-all duration-200" do %>
- <%= image_tag party.image, alt: party.name, class: "w-full h-full object-cover" if party.image.present? %> + <%= image_tag event.image, alt: event.name, class: "w-full h-full object-cover" if event.image.present? %>

- <%= party.name %> + <%= event.name %>

- <%= l(party.start_time, format: :short) %> + <%= l(event.start_time, format: :short) %>

diff --git a/app/views/components/_footer.html.erb b/app/views/components/_footer.html.erb index 6afdaf0..a9d6524 100755 --- a/app/views/components/_footer.html.erb +++ b/app/views/components/_footer.html.erb @@ -1,83 +1,41 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/app/views/components/_header.html.erb b/app/views/components/_header.html.erb index 4343800..3e31ac7 100755 --- a/app/views/components/_header.html.erb +++ b/app/views/components/_header.html.erb @@ -1,137 +1,137 @@ -
-
-
diff --git a/app/views/parties/index.html.erb b/app/views/events/index.html.erb similarity index 77% rename from app/views/parties/index.html.erb rename to app/views/events/index.html.erb index e2a0e34..9d0c789 100755 --- a/app/views/parties/index.html.erb +++ b/app/views/events/index.html.erb @@ -1,37 +1,37 @@

Événements à venir

- <% if @parties.any? %> + <% if @events.any? %>
- <% @parties.each do |party| %> + <% @events.each do |event| %>
-

<%= party.name %>

-

<%= party.user.email %>

+

<%= event.name %>

+

<%= event.user.email %>

- <%= party.start_time.strftime("%d/%m/%Y") %> + <%= event.start_time.strftime("%d/%m/%Y") %>
-

<%= party.description.truncate(100) %>

+

<%= event.description.truncate(100) %>

- <% if party.ticket_types.any? %> + <% if event.ticket_types.any? %>

- À partir de <%= format_price(party.ticket_types.minimum(:price_cents)) %>€ + À partir de <%= format_price(event.ticket_types.minimum(:price_cents)) %>€

<% else %>

Pas de billets disponibles

<% end %>
- <%= link_to "Voir les détails", party_path(party.slug, party), class: "inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-full shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500" %> + <%= link_to "Voir les détails", event_path(event.slug, event), class: "inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-full shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500" %>
@@ -39,8 +39,8 @@
- <%# paginate @parties, theme: 'twitter_bootstrap' %> - <%= paginate @parties %> + <%# paginate @events, theme: 'twitter_bootstrap' %> + <%= paginate @events %>
<% else %>
diff --git a/app/views/parties/show.html.erb b/app/views/events/show.html.erb similarity index 90% rename from app/views/parties/show.html.erb rename to app/views/events/show.html.erb index cce3b3e..40b4e7d 100755 --- a/app/views/parties/show.html.erb +++ b/app/views/events/show.html.erb @@ -1,4 +1,4 @@ -
+
@@ -32,13 +32,13 @@
- +
-

<%= @party.name %>

+

<%= @event.name %>

- <% if @party.image.present? %> + <% if @event.image.present? %>
- <%= image_tag @party.image, class: "w-full h-96 object-cover" %> + <%= image_tag @event.image, class: "w-full h-96 object-cover" %>

Event Details

@@ -47,13 +47,13 @@ - <%= @party.venue_name %> + <%= @event.venue_name %>
- <%= @party.start_time.strftime("%B %d, %Y at %I:%M %p") %> + <%= @event.start_time.strftime("%B %d, %Y at %I:%M %p") %>
@@ -63,7 +63,7 @@

Description

-

<%= @party.description %>

+

<%= @event.description %>

@@ -73,7 +73,7 @@ Location: - <%= @party.venue_address %> + <%= @event.venue_address %>
@@ -81,7 +81,7 @@ Date: - <%= @party.start_time.strftime("%B %d, %Y") %> + <%= @event.start_time.strftime("%B %d, %Y") %>
@@ -93,7 +93,7 @@

Available Tickets

- <% @party.ticket_types.each do |ticket_type| %> + <% @event.ticket_types.each do |ticket_type| %> <% sold_out = ticket_type.quantity <= ticket_type.tickets.count %> <% remaining = ticket_type.quantity - ticket_type.tickets.count %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 874fffd..d8654d7 100755 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,5 +1,5 @@ - + <%= content_for(:title) || "Aperonight" %> @@ -8,23 +8,33 @@ <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= yield :head %> + + + + + + + + + <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %> <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> + <%# Includes all stylesheet files in app/assets/stylesheets %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %> - - -
+ + +
<%= render "components/header" %> -
+
<% if flash.any? %> -
+
<%= render "shared/flash_messages" %>
<% end %> @@ -34,12 +44,20 @@
-
-
+
+
<%= render "components/footer" %>
-
+ + diff --git a/app/views/pages/components.html.erb b/app/views/pages/components.html.erb new file mode 100755 index 0000000..31336d5 --- /dev/null +++ b/app/views/pages/components.html.erb @@ -0,0 +1,247 @@ +
+
+

Aperonight Design System

+

+ A comprehensive collection of reusable components for premium event booking experiences. +

+
+ + +
+

Buttons

+
+
+

Primary Buttons

+
+ + + +
+
+ +
+

Secondary Buttons

+
+ + + +
+
+ +
+

Outline Buttons

+
+ + + +
+
+ +
+

Ghost Buttons

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

Form Elements

+
+
+

Input Fields

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

Validation States

+
+ + +
+ + + + + + Email is valid +
+
+ +
+ + +
+ + + + + + + Password must be at least 8 characters +
+
+ +
+ + +
+ + + + + + + Please complete phone number +
+
+
+
+
+ + +
+

Badges & Tags

+
+
+

Event Status Badges

+
+ Available + Limited + Sold Out + ★ Featured + VIP +
+
+ +
+

Price Tags

+
+
€15
+
€35
+
€55
+
+ €60 + €45 +
+
+
+
+
+ + +
+

Cards

+
+
+
+

Basic Card

+

Simple card with header and content

+
+
+

This is a basic card component that can be used for various content types. It includes proper spacing and hover effects.

+ +
+
+ +
+
+ Event Image +
+
+
+ ★ Featured + Available +
+

Networking Event

+

Join professionals for an evening of networking and insights.

+
+ €35 + +
+
+
+ +
+

User Profile

+
+
+
+
+
+
+

Alex Johnson

+

Senior Developer

+
+ +
+ + + + + + + + + + + + + + + +
+ 4.2 +
+
+
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/app/views/pages/dashboard.html.erb b/app/views/pages/dashboard.html.erb index b62c33c..431bc89 100755 --- a/app/views/pages/dashboard.html.erb +++ b/app/views/pages/dashboard.html.erb @@ -4,7 +4,7 @@

Tableau de bord

- <%= render partial: 'components/metric_card', locals: { title: "Événements disponibles", value: @available_parties, classes: "from-purple-100 to-indigo-100" } %> + <%= render partial: 'components/metric_card', locals: { title: "Événements disponibles", value: @available_events, classes: "from-purple-100 to-indigo-100" } %> <%= render partial: 'components/metric_card', locals: { title: "Événements aujourd'hui", value: @events_this_week, classes: "from-purple-100 to-indigo-100" } %> @@ -13,17 +13,17 @@
- +

Évenements du jour

- <% if @today_parties.any? %> + <% if @today_events.any? %>
    - <% @today_parties.each do |party| %> + <% @today_events.each do |event| %>
  • - <%= render partial: 'components/party_item', locals: { party: party } %> + <%= render partial: 'components/event_item', locals: { event: event } %>
  • <% end %>
@@ -33,17 +33,17 @@
- +

Évenements de demain

- <% if @tomorrow_parties.any? %> + <% if @tomorrow_events.any? %>
    - <% @tomorrow_parties.each do |party| %> + <% @tomorrow_events.each do |event| %>
  • - <%= render partial: 'components/party_item', locals: { party: party } %> + <%= render partial: 'components/event_item', locals: { event: event } %>
  • <% end %>
@@ -53,24 +53,24 @@
- +

Autres évenements à venir

- <% if @other_parties.any? %> + <% if @other_events.any? %>
    - <% @other_parties.each do |party| %> + <% @other_events.each do |event| %>
  • - <%= render partial: 'components/party_item', locals: { party: party } %> + <%= render partial: 'components/event_item', locals: { event: event } %>
  • <% end %>
- <%= paginate @other_parties %> + <%= paginate @other_events %>
<% else %>

Aucune autre partie à venir.

diff --git a/app/views/pages/events.html.erb b/app/views/pages/events.html.erb index ebe0dbe..c7157c6 100755 --- a/app/views/pages/events.html.erb +++ b/app/views/pages/events.html.erb @@ -1,46 +1,217 @@ -
-

Upcoming Events

- - <% if @parties.any? %> -
- <% @parties.each do |party| %> -
- <% if party.image.present? %> - <%= party.name %> - <% else %> -
- No Image -
- <% end %> - -
-

<%= party.name %>

-

<%= party.description.truncate(100) %>

- -
-
-

<%= party.venue_name %>

-

<%= party.venue_address %>

-
-
- - <%= party.state.humanize %> - -
-
-
-
- <% end %> +<% content_for :title, "All Events - Aperonight" %> + +
+
+
+

All Events

+

Discover all our upcoming afterwork events tailored to your interests.

- -
- <%= paginate @parties %> +
+ +
+ + +
- <% else %> -
-

No events found

-

Check back later for upcoming events.

+ +
+ +
+ Tech Networking Night +
+
+

Tech & Innovation Networking Night

+ ★ Featured +
+
+ + Thu, Mar 15 • 18:30 +
+
+ + Le Perchoir Marais, Paris +
+
+ €35 + +
+
+
+ + +
+ Creative Afterwork +
+
+

Creative Directors Exclusive Meetup

+ VIP +
+
+ + Fri, Mar 16 • 19:00 +
+
+ + Atelier Des Lumières, Paris +
+
+ €65 + +
+
+
+ + +
+ Wine Tasting +
+
+

Wine & Business Premium Tasting

+ Available +
+
+ + Sat, Mar 18 • 17:00 +
+
+ + Cave Legrand, Paris +
+
+ €55 + +
+
+
+ + +
+ Startup Pitch Night +
+
+

Startup Pitch Night

+ Limited +
+
+ + Mon, Mar 20 • 18:00 +
+
+ + Station F, Paris +
+
+ €25 + +
+
+
+ + +
+ Jazz & Cocktails +
+
+

Jazz & Cocktails Evening

+ Available +
+
+ + Wed, Mar 22 • 20:00 +
+
+ + Blue Note, Paris +
+
+ €40 + +
+
+
+ + +
+ Art & Wine +
+
+

Art & Wine Gallery Night

+ ★ Featured +
+
+ + Fri, Mar 24 • 19:30 +
+
+ + Gallery Lafayette, Paris +
+
+ €30 + +
+
+
- <% end %> -
\ No newline at end of file + +
+ +
+
+
+ + \ No newline at end of file diff --git a/app/views/pages/home.html.erb b/app/views/pages/home.html.erb index 3bf7786..94de205 100755 --- a/app/views/pages/home.html.erb +++ b/app/views/pages/home.html.erb @@ -1,250 +1,211 @@ +<% content_for :title, "Aperonight - Découvrez des événements après-travail de luxe" %> + -
-
-
-

- Découvrez les afterworks et soirée - - à Paris - -

-

- Les meilleures soirées, concerts et afterworks de Paris en un clic. Réservez vos places et vivez des expériences uniques. -

-
- <%= link_to "Explorer les soirées", parties_path, class: "bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-semibold py-4 px-8 rounded-full transition-all duration-300 transform hover:scale-105 shadow-lg" %> - <%= link_to "Voir les concerts", "#", class: "bg-white border border-neutral-300 hover:border-purple-300 text-neutral-700 font-semibold py-4 px-8 rounded-full transition-all duration-300" %> +
+
+
+

Découvrez les afterworks à Paris

+

Connectez-vous avec des professionnels, explorez des lieux uniques et créez des expériences mémorables lors d'événements après-travail soigneusement sélectionnés dans votre ville.

+ +
+ <%= link_to "Explorer les événements", events_path, class: "btn btn-lg btn-primary" %> + <%= link_to "Organiser un événement", "#", class: "btn btn-lg btn-secondary" %> +
- - -
-
-
-

- Des chiffres qui parlent -

-

- La plateforme préférée des Parisiens pour vivre la nuit -

+ +<%= render "components/event_finder" %> + + + - -
-
-
- 0% + +
+
+
+

Why Choose Aperonight?

+

We curate premium experiences that connect professionals and create lasting relationships.

+
+
+
+
+
-

Taux de remplissage

+

Premium Curation

+

Every event is carefully selected and designed to provide exceptional value and networking opportunities.

- -
-
- 0 +
+
+
-

Arrondissements

+

Secure & Trusted

+

Safe payments, verified venues, and trusted community with comprehensive insurance coverage.

- -
-
- 0 +
+
+
-

Établissements partenaires

+

Quality Networking

+

Connect with verified professionals, entrepreneurs, and industry leaders in intimate settings.

- -
-
- 0% +
+
+
-

Satisfaction client

+

Instant Booking

+

Seamless reservation process with instant confirmation and easy event management.

- -