Loading...
Loading...
Create interactive, production-ready UI mockups and prototypes using NuxtJS 4 (Vue) or Next.js (React), TypeScript, and TailwindCSS v4. Use when building web mockups, prototypes, landing pages, dashboards, admin panels, or interactive UI demonstrations. Trigger when users mention "create mockup", "build prototype", "interactive demo", "UI prototype", "design to code", or need rapid frontend development with modern tooling. Prefer NuxtJS for Vue-based projects; use Next.js when users mention ReactJS.
npx skill4agent add dauquangthanh/hanoi-rainbow mockup-creation# Create NuxtJS 4 project (TypeScript enabled by default)
npx nuxi@latest init my-mockup
cd my-mockup
npm installnpm install -D tailwindcss @tailwindcss/vite// https://nuxt.com/docs/4.x/api/configuration/nuxt-config
import tailwindcss from '@tailwindcss/vite'
export default defineNuxtConfig({
devtools: { enabled: true },
typescript: {
strict: true,
typeCheck: true
},
// Auto-import components and composables
components: [
{
path: '~/components',
pathPrefix: false,
},
],
// App configuration
app: {
head: {
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1',
title: 'My Mockup',
meta: [
{ name: 'description', content: 'My mockup description' }
],
}
},
// Vite configuration with TailwindCSS v4
vite: {
plugins: [
tailwindcss()
],
css: {
devSourcemap: true
}
}
})@import "tailwindcss";// In nuxt.config.ts, add to the config:
export default defineNuxtConfig({
css: ['~/assets/css/main.css'],
// ... rest of config
})npm run dev
# Opens http://localhost:3000# Create Next.js project (uses recommended defaults with TypeScript and TailwindCSS)
npx create-next-app@latest my-mockup --yes
cd my-mockupnpx create-next-app@latest my-mockup
# Choose: TypeScript: Yes, TailwindCSS: Yes, App Router: Yesnpm install -D tailwindcss@next @tailwindcss/postcss@nextimport type { Config } from 'tailwindcss'
export default {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
} satisfies Configexport default {
plugins: {
'@tailwindcss/postcss': {},
},
}@import "tailwindcss";npm run dev
# Opens http://localhost:3000my-mockup/
├── components/
│ ├── layout/ # Header, Footer, Sidebar (auto-imported)
│ ├── ui/ # Button, Card, Modal, Input (auto-imported)
│ └── sections/ # Hero, Features, Testimonials (auto-imported)
├── pages/ # File-based routing (auto-routed)
├── composables/ # Shared logic (auto-imported)
├── layouts/ # Layout templates (default.vue, dashboard.vue)
└── types/ # TypeScript interfacesmy-mockup/
├── app/
│ ├── layout.tsx # Root layout (required)
│ ├── page.tsx # Home page
│ ├── dashboard/
│ │ └── page.tsx # /dashboard route
│ └── globals.css # TailwindCSS imports
├── components/
│ ├── layout/ # Header, Footer, Sidebar
│ ├── ui/ # Button, Card, Modal, Input
│ └── sections/ # Hero, Features, Testimonials
├── lib/ # Utilities and shared logic
└── types/ # TypeScript interfaces<script setup lang="ts">
// No need to import 'computed' - auto-imported by Nuxt
interface Props {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md'
})
const buttonClasses = computed(() => {
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-600 text-white hover:bg-gray-700',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50'
}
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
}
return `font-semibold rounded-lg transition ${variants[props.variant]} ${sizes[props.size]}`
})
</script>
<template>
<button :class="buttonClasses">
<slot />
</button>
</template><template>
<div>
<!-- Auto-imported as UiButton from components/ui/Button.vue -->
<UiButton variant="primary" size="lg">
Get Started
</UiButton>
</div>
</template>import { ButtonHTMLAttributes } from 'react'
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
children: React.ReactNode
}
export function Button({
variant = 'primary',
size = 'md',
children,
className = '',
...props
}: ButtonProps) {
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-600 text-white hover:bg-gray-700',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50'
}
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
}
const buttonClasses = `font-semibold rounded-lg transition ${variants[variant]} ${sizes[size]} ${className}`
return (
<button className={buttonClasses} {...props}>
{children}
</button>
)
}import { Button } from '@/components/ui/Button'
export default function Home() {
return (
<div>
<Button variant="primary" size="lg">
Get Started
</Button>
</div>
)
}sm:md:lg:xl:2xl:<template>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Auto-imported as UiCard from components/ui/Card.vue -->
<UiCard v-for="item in items" :key="item.id" :data="item" />
</div>
</template>
<script setup lang="ts">
const items = ref([...])
</script>import { Card } from '@/components/ui/Card'
export default function FeaturesSection() {
const items = [
{ id: 1, title: 'Feature 1', description: '...' },
// ...
]
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{items.map((item) => (
<Card key={item.id} data={item} />
))}
</div>
)
}// Auto-imported by Nuxt - no need to import 'ref'
export function useModal() {
const isOpen = ref(false)
const open = () => { isOpen.value = true }
const close = () => { isOpen.value = false }
return { isOpen, open, close }
}<script setup lang="ts">
// Auto-imported - no import statement needed
const { isOpen, open, close } = useModal()
</script>import { useState } from 'react'
export function useModal() {
const [isOpen, setIsOpen] = useState(false)
const open = () => setIsOpen(true)
const close = () => setIsOpen(false)
return { isOpen, open, close }
}'use client' // Mark as Client Component for interactivity
import { useModal } from '@/lib/hooks/useModal'
export default function MyComponent() {
const { isOpen, open, close } = useModal()
return (
<div>
<button onClick={open}>Open Modal</button>
{isOpen && <Modal onClose={close} />}
</div>
)
}npm run build # Build for production (.output/)
npm run preview # Preview production build
npm run generate # Generate static site (SSG)npm run build # Build for production (.next/)
npm run start # Start production server (SSR)
# For static export (SSG), add to next.config.js:
# output: 'export'<template>
<div class="min-h-screen flex flex-col">
<LayoutHeader />
<main class="flex-1">
<slot />
</main>
<LayoutFooter />
</div>
</template><template>
<div>
<SectionsHero />
<SectionsFeatures />
</div>
</template>import { Header } from '@/components/layout/Header'
import { Footer } from '@/components/layout/Footer'
import './globals.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className="min-h-screen flex flex-col">
<Header />
<main className="flex-1">{children}</main>
<Footer />
</body>
</html>
)
}import { Hero } from '@/components/sections/Hero'
import { Features } from '@/components/sections/Features'
export default function Home() {
return (
<div>
<Hero />
<Features />
</div>
)
}<template>
<div class="flex h-screen bg-gray-100">
<LayoutSidebar class="w-64 bg-white shadow-lg" />
<div class="flex-1 flex flex-col">
<LayoutTopBar class="bg-white shadow-sm" />
<main class="flex-1 overflow-y-auto p-6">
<slot />
</main>
</div>
</div>
</template><script setup lang="ts">
definePageMeta({
layout: 'dashboard'
})
</script>
<template>
<div>
<!-- Dashboard content -->
</div>
</template>import { Sidebar } from '@/components/layout/Sidebar'
import { TopBar } from '@/components/layout/TopBar'
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex h-screen bg-gray-100">
<Sidebar className="w-64 bg-white shadow-lg" />
<div className="flex-1 flex flex-col">
<TopBar className="bg-white shadow-sm" />
<main className="flex-1 overflow-y-auto p-6">
{children}
</main>
</div>
</div>
)
}export default function DashboardPage() {
return (
<div>
{/* Dashboard content */}
</div>
)
}blue-600gray-600green-600yellow-600red-600gray-100gray-900p-1p-2p-4p-6p-8text-4xltext-3xltext-2xltext-xltext-basefont-normalfont-mediumfont-semiboldfont-boldscripts/# Linux/macOS - Vue component
./scripts/create-component.sh ComponentName ui vue
# Linux/macOS - React component
./scripts/create-component.sh ComponentName ui react
# Windows - Vue component
.\scripts\create-component.ps1 -ComponentName ComponentName -Type ui -Framework vue
# Windows - React component
.\scripts\create-component.ps1 -ComponentName ComponentName -Type ui -Framework react# Linux/macOS
./scripts/build-deploy.sh
# Windows
.\scripts\build-deploy.ps1npx nuxi add component ComponentName # Add new component
npx nuxi add page pageName # Add new page
npx nuxi add layout layoutName # Add new layout# No built-in CLI for components, use scripts above
# Or manually create files in app/ or components/@import "tailwindcss";@tailwindcss/vitenpx nuxi preparenpx nuxi preparerm -rf .nuxt
npx nuxi prepare
npm run devrm -rf .nuxt node_modules/.cache
npm run devdefineAsyncComponent(() => import('./Component.vue'))npx nuxi analyzetailwind.config.ts@import "tailwindcss";.nextrm -rf .next && npm run devnpm run build'use client'rm -rf .next node_modules/.cache
npm install
npm run buildconst Component = dynamic(() => import('./Component'))npm run builduseHead()useSeoMeta()generateMetadata()<Image>