diff --git a/yourwork/.amazonq/cli-agents/default-agent.json b/yourwork/.amazonq/cli-agents/default-agent.json index 30b69b7..5ed9514 100644 --- a/yourwork/.amazonq/cli-agents/default-agent.json +++ b/yourwork/.amazonq/cli-agents/default-agent.json @@ -8,8 +8,6 @@ ], "allowedTools": [ "fs_read", - "fs_write", - "execute_bash", "use_aws", "report_issue" ], diff --git a/yourwork/template/DEPLOYMENT_GUIDE.md b/yourwork/template/DEPLOYMENT_GUIDE.md index 5b45763..afca609 100644 --- a/yourwork/template/DEPLOYMENT_GUIDE.md +++ b/yourwork/template/DEPLOYMENT_GUIDE.md @@ -35,10 +35,10 @@ template/ ## 技術スタック -- **フロントエンド**: React 18 + TypeScript +- **フロントエンド**: React + TypeScript - **ビルドツール**: Vite -- **スタイリング**: Tailwind CSS + Framer Motion -- **ルーティング**: React Router v6 +- **スタイリング**: Tailwind CSS v4+ (CSS-first configuration) + Framer Motion +- **ルーティング**: React Router - **テスト**: Vitest + Playwright - **ホスティング**: AWS S3 + CloudFront - **インフラ**: AWS CloudFormation @@ -107,13 +107,27 @@ VITE_ENVIRONMENT=development # 環境名 ### 5. スタイリング/ブランディング -#### **tailwind.config.js** -- カラーパレットをブランドカラーに変更 -- フォントファミリーの設定 +#### **Tailwind CSS v4 設定** (`src/styles/globals.css`) +Tailwind CSS v4では設定ファイル不要のCSS-first approach を採用: -#### **src/styles/globals.css** -- カスタムCSS変数の追加 -- ブランド固有のスタイル定義 +```css +@import "tailwindcss"; + +@theme { + /* カスタムカラーパレット */ + --color-primary-500: #0ea5e9; /* ブランドカラーに変更 */ + --color-primary-600: #0284c7; + + /* カスタムアニメーション */ + --animate-fade-in: fadeIn 0.5s ease-in-out; +} +``` + +**重要**: +- ❌ `tailwind.config.js` は不要(削除済み) +- ❌ `postcss.config.js` は不要(削除済み) +- ❌ `@tailwindcss/*` プラグインは不要(v4で内蔵) +- ✅ CSS内で `@theme {}` ブロックで設定 ### 6. ランディングページのカスタマイズ @@ -127,8 +141,8 @@ VITE_ENVIRONMENT=development # 環境名 ## 前提条件 ### 必要なツール -- Node.js 18.x 以上 -- npm 8.x 以上 +- Node.js 20.x 以上 +- npm 10.x 以上 - AWS CLI v2 - AWS アカウントと適切な権限 diff --git a/yourwork/template/app/cloudformation.yaml b/yourwork/template/app/cloudformation.yaml index 6ed8f00..f73ffa0 100644 --- a/yourwork/template/app/cloudformation.yaml +++ b/yourwork/template/app/cloudformation.yaml @@ -46,7 +46,7 @@ Resources: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: - Name: !Sub '${ProjectName}-${Environment}-oac' + Name: !Sub '${ProjectName}-${Environment}-oac-${AWS::AccountId}' OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4 @@ -139,7 +139,7 @@ Resources: Type: AWS::CloudFront::ResponseHeadersPolicy Properties: ResponseHeadersPolicyConfig: - Name: !Sub '${ProjectName}-${Environment}-security-headers' + Name: !Sub '${ProjectName}-${Environment}-security-headers-${AWS::AccountId}' SecurityHeadersConfig: StrictTransportSecurity: AccessControlMaxAgeSec: 31536000 @@ -176,28 +176,28 @@ Outputs: Description: 'S3 Bucket name for uploading files' Value: !Ref S3Bucket Export: - Name: !Sub '${ProjectName}-${Environment}-bucket-name' + Name: !Sub '${ProjectName}-${Environment}-bucket-name-${AWS::AccountId}' CloudFrontDistributionId: Description: 'CloudFront Distribution ID' Value: !Ref CloudFrontDistribution Export: - Name: !Sub '${ProjectName}-${Environment}-distribution-id' + Name: !Sub '${ProjectName}-${Environment}-distribution-id-${AWS::AccountId}' CloudFrontDomainName: Description: 'CloudFront Distribution Domain Name' Value: !GetAtt CloudFrontDistribution.DomainName Export: - Name: !Sub '${ProjectName}-${Environment}-domain-name' + Name: !Sub '${ProjectName}-${Environment}-domain-name-${AWS::AccountId}' WebsiteURL: Description: 'Website URL' Value: !Sub 'https://${CloudFrontDistribution.DomainName}' Export: - Name: !Sub '${ProjectName}-${Environment}-website-url' + Name: !Sub '${ProjectName}-${Environment}-website-url-${AWS::AccountId}' S3BucketRegion: Description: 'S3 Bucket Region' Value: !Ref 'AWS::Region' Export: - Name: !Sub '${ProjectName}-${Environment}-bucket-region' \ No newline at end of file + Name: !Sub '${ProjectName}-${Environment}-bucket-region-${AWS::AccountId}' \ No newline at end of file diff --git a/yourwork/template/app/package.json b/yourwork/template/app/package.json index 2e3a3ee..9d29c14 100644 --- a/yourwork/template/app/package.json +++ b/yourwork/template/app/package.json @@ -22,9 +22,7 @@ "dependencies": { "@headlessui/react": "^2.2.8", "@heroicons/react": "^2.2.0", - "@tailwindcss/aspect-ratio": "^0.4.2", - "@tailwindcss/forms": "^0.5.10", - "@tailwindcss/typography": "^0.5.18", + "clsx": "^2.1.1", "framer-motion": "^12.23.16", "lucide-react": "^0.544.0", @@ -47,14 +45,15 @@ "@vitejs/plugin-react": "^5.0.3", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", - "autoprefixer": "^10.4.21", + + "@tailwindcss/vite": "^4.1.13", "eslint": "^9.36.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "jsdom": "^27.0.0", "msw": "^2.11.2", - "postcss": "^8.5.6", + "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", "tailwindcss": "^4.1.13", diff --git a/yourwork/template/app/postcss.config.js b/yourwork/template/app/postcss.config.js deleted file mode 100644 index e99ebc2..0000000 --- a/yourwork/template/app/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} \ No newline at end of file diff --git a/yourwork/template/app/src/components/app/HomePage.tsx b/yourwork/template/app/src/components/app/HomePage.tsx index 5b8e041..c282028 100644 --- a/yourwork/template/app/src/components/app/HomePage.tsx +++ b/yourwork/template/app/src/components/app/HomePage.tsx @@ -1,65 +1,85 @@ -import { useEffect, useRef } from 'react'; -import LoadingSpinner from '@/components/common/LoadingSpinner'; +import React from 'react'; +import { motion } from 'framer-motion'; import { mlewTracker } from '../../services/mlewTracker'; -function HomePage() { - const hasTracked = useRef(false); +const HomePage: React.FC = () => { + const handleGetStarted = () => { + mlewTracker.trackCTAClick('Get Started', 'home-page', '/features'); + }; - useEffect(() => { - // Track app page view only once (prevent React Strict Mode double execution) - if (!hasTracked.current) { - hasTracked.current = true; - mlewTracker.trackPageView('app-home'); - } - }, []); + const handleLearnMore = () => { + mlewTracker.trackClick('learn-more-button', { + source: 'home-page', + destination: 'features' + }); + }; return ( -
-
-
-

- MLEW React App Template +
+
+ +

+ + MLEW React App Template +

-

- Modern React TypeScript application template with analytics integration -

-
- +

+ A modern, production-ready React application template with TypeScript, + Tailwind CSS, and integrated analytics tracking. +

+ +
+ + Get Started + + + + Learn More +
-
-
-

- React + TypeScript -

-

- Built with modern React and TypeScript for type-safe development -

+
+
+
+

Fast Development

+

Built with Vite for lightning-fast development experience

-
-

- Tailwind CSS -

-

- Utility-first CSS framework for rapid UI development -

+
+
🎨
+

Modern Design

+

Tailwind CSS v4 with beautiful, responsive components

-
-

- Analytics Ready -

-

- Integrated MLEWW3 Tracker for comprehensive analytics -

+
+
📊
+

Analytics Ready

+

Integrated MLEW Tracker for comprehensive user analytics

-
+
); -} +}; -export default HomePage; \ No newline at end of file +export default HomePage; diff --git a/yourwork/template/app/src/styles/globals.css b/yourwork/template/app/src/styles/globals.css index 50c7376..b00d5ce 100644 --- a/yourwork/template/app/src/styles/globals.css +++ b/yourwork/template/app/src/styles/globals.css @@ -1,219 +1,39 @@ -@import 'tailwindcss/base'; -@import 'tailwindcss/components'; -@import 'tailwindcss/utilities'; +@import "tailwindcss"; -@layer base { - * { - @apply border-gray-200; - } +@theme { + --color-primary-50: #f0f9ff; + --color-primary-100: #e0f2fe; + --color-primary-200: #bae6fd; + --color-primary-300: #7dd3fc; + --color-primary-400: #38bdf8; + --color-primary-500: #0ea5e9; + --color-primary-600: #0284c7; + --color-primary-700: #0369a1; + --color-primary-800: #075985; + --color-primary-900: #0c4a6e; + --animate-fade-in: fadeIn 0.5s ease-in-out; + --animate-slide-up: slideUp 0.5s ease-out; +} + +@layer base { html { @apply scroll-smooth; - font-feature-settings: 'cv11', 'ss01'; - font-variation-settings: 'opsz' 32; } body { @apply bg-white text-gray-900 font-sans antialiased; - @apply selection:bg-primary-100 selection:text-primary-900; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - /* Custom scrollbar */ - ::-webkit-scrollbar { - @apply w-2; - } - - ::-webkit-scrollbar-track { - @apply bg-gray-100; - } - - ::-webkit-scrollbar-thumb { - @apply bg-gray-300 rounded-full hover:bg-gray-400; - } - - /* Focus styles */ - .focus-ring { - @apply focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2; - } - - /* Typography improvements */ - h1, h2, h3, h4, h5, h6 { - @apply font-semibold tracking-tight; - text-wrap: balance; - } - - p { - text-wrap: pretty; } } @layer components { - /* Button components */ - .btn { - @apply inline-flex items-center justify-center px-6 py-3 text-sm font-medium rounded-lg; - @apply focus:outline-none focus:ring-2 focus:ring-offset-2; - @apply disabled:opacity-50 disabled:cursor-not-allowed; - @apply transition-all duration-200 ease-in-out; - } - - .btn-primary { - @apply btn bg-primary-600 text-white hover:bg-primary-700; - @apply focus:ring-primary-500 active:bg-primary-800; - @apply shadow-sm hover:shadow; - } - - .btn-secondary { - @apply btn bg-white text-gray-900 border border-gray-300 hover:bg-gray-50; - @apply focus:ring-gray-500 active:bg-gray-100; - } - - .btn-ghost { - @apply btn bg-transparent text-gray-700 hover:bg-gray-100; - @apply focus:ring-gray-500 active:bg-gray-200; - } - - .btn-lg { - @apply px-8 py-4 text-base; - } - - .btn-sm { - @apply px-4 py-2 text-xs; - } - - /* Card components */ .card { @apply bg-white rounded-xl shadow-sm border border-gray-100; @apply transition-all duration-200; } - .card-hover { - @apply hover:shadow-lg hover:border-gray-200 hover:-translate-y-1; - } - - /* Container */ - .container-custom { - @apply mx-auto px-4 sm:px-6 lg:px-8; - } - - /* Gradient text */ .gradient-text { - @apply bg-gradient-to-r from-primary-600 to-blue-600 bg-clip-text text-transparent; - } - - /* Hero background pattern */ - .hero-pattern { - background-image: - radial-gradient(circle at 25px 25px, rgba(59, 130, 246, 0.1) 2px, transparent 0), - radial-gradient(circle at 75px 75px, rgba(99, 102, 241, 0.1) 2px, transparent 0); - background-size: 100px 100px; - } - - /* Section spacing */ - .section-padding { - @apply py-16 sm:py-20 lg:py-24; - } - - /* Animation utilities */ - .animate-fade-in-up { - animation: fadeInUp 0.6s ease-out forwards; - } - - .animate-fade-in-up-delay-1 { - animation: fadeInUp 0.6s ease-out 0.1s forwards; - opacity: 0; - } - - .animate-fade-in-up-delay-2 { - animation: fadeInUp 0.6s ease-out 0.2s forwards; - opacity: 0; - } - - .animate-fade-in-up-delay-3 { - animation: fadeInUp 0.6s ease-out 0.3s forwards; - opacity: 0; - } - - /* Loading spinner */ - .spinner { - @apply inline-block w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin; - } - - /* Feature grid */ - .feature-grid { - @apply grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-8; - } - - /* Testimonial card */ - .testimonial-card { - @apply card p-6 lg:p-8 text-center; - } - - /* Pricing card */ - .pricing-card { - @apply card p-6 lg:p-8 relative; - } - - .pricing-card-popular { - @apply ring-2 ring-primary-500 shadow-lg; - } - - /* Form components */ - .form-input { - @apply w-full px-4 py-3 border border-gray-300 rounded-lg; - @apply focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500; - @apply disabled:bg-gray-50 disabled:text-gray-500 disabled:cursor-not-allowed; - @apply placeholder:text-gray-400; - @apply transition-colors duration-200; - } - - .form-textarea { - @apply form-input resize-none; - } - - .form-label { - @apply block text-sm font-medium text-gray-700 mb-2; - } - - .form-error { - @apply mt-1 text-sm text-red-600; - } -} - -@layer utilities { - /* Text utilities */ - .text-balance { - text-wrap: balance; - } - - .text-pretty { - text-wrap: pretty; - } - - /* Layout utilities */ - .safe-area-inset { - padding-top: env(safe-area-inset-top); - padding-bottom: env(safe-area-inset-bottom); - padding-left: env(safe-area-inset-left); - padding-right: env(safe-area-inset-right); - } - - /* Glass morphism */ - .glass { - @apply bg-white/80 backdrop-blur-md border border-white/20; - } - - /* Hide scrollbar */ - .no-scrollbar { - -ms-overflow-style: none; - scrollbar-width: none; - } - - .no-scrollbar::-webkit-scrollbar { - display: none; + @apply bg-gradient-to-r from-blue-600 to-indigo-600 bg-clip-text text-transparent; } } @@ -228,76 +48,3 @@ transform: translateY(0); } } - -@keyframes slideInLeft { - from { - opacity: 0; - transform: translateX(-30px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes slideInRight { - from { - opacity: 0; - transform: translateX(30px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes scaleIn { - from { - opacity: 0; - transform: scale(0.95); - } - to { - opacity: 1; - transform: scale(1); - } -} - -@keyframes pulse-glow { - 0%, 100% { - box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); - } - 50% { - box-shadow: 0 0 40px rgba(59, 130, 246, 0.6); - } -} - -/* Reduced motion */ -@media (prefers-reduced-motion: reduce) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} - -/* High contrast mode */ -@media (prefers-contrast: high) { - .card { - @apply border-2 border-gray-900; - } - - .btn-primary { - @apply border border-white; - } -} - -/* Print styles */ -@media print { - .no-print { - @apply hidden; - } - - * { - @apply text-black bg-white; - } -} \ No newline at end of file diff --git a/yourwork/template/app/tailwind.config.js b/yourwork/template/app/tailwind.config.js deleted file mode 100644 index 97e2063..0000000 --- a/yourwork/template/app/tailwind.config.js +++ /dev/null @@ -1,61 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: { - colors: { - primary: { - 50: '#f0f9ff', - 100: '#e0f2fe', - 200: '#bae6fd', - 300: '#7dd3fc', - 400: '#38bdf8', - 500: '#0ea5e9', - 600: '#0284c7', - 700: '#0369a1', - 800: '#075985', - 900: '#0c4a6e', - }, - secondary: { - 50: '#fdf2f8', - 100: '#fce7f3', - 200: '#fbcfe8', - 300: '#f9a8d4', - 400: '#f472b6', - 500: '#ec4899', - 600: '#db2777', - 700: '#be185d', - 800: '#9d174d', - 900: '#831843', - }, - }, - animation: { - 'fade-in': 'fadeIn 0.5s ease-in-out', - 'slide-up': 'slideUp 0.5s ease-out', - 'slide-down': 'slideDown 0.3s ease-out', - }, - keyframes: { - fadeIn: { - '0%': { opacity: '0' }, - '100%': { opacity: '1' }, - }, - slideUp: { - '0%': { transform: 'translateY(20px)', opacity: '0' }, - '100%': { transform: 'translateY(0)', opacity: '1' }, - }, - slideDown: { - '0%': { transform: 'translateY(-10px)', opacity: '0' }, - '100%': { transform: 'translateY(0)', opacity: '1' }, - }, - }, - }, - }, - plugins: [ - require('@tailwindcss/forms'), - require('@tailwindcss/typography'), - require('@tailwindcss/aspect-ratio'), - ], -} \ No newline at end of file diff --git a/yourwork/template/app/vite.config.ts b/yourwork/template/app/vite.config.ts index 0e674ef..f963897 100644 --- a/yourwork/template/app/vite.config.ts +++ b/yourwork/template/app/vite.config.ts @@ -1,10 +1,11 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; +import tailwindcss from '@tailwindcss/vite'; import path from 'path'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [react(), tailwindcss()], resolve: { alias: { '@': path.resolve(__dirname, './src'),